[wasm] Early compilation of import wrappers

Compile import wrappers during module compilation by introducing import
wrapper compilation units, the goal being to reduce instantiation time.

For each wrapper, we assume the imported function is going to be a
kJSFunctionArityMatchSloppy at instantiation time, which should be the
most common case. If the function turns out to have a different kind the
wrapper is going to be recompiled with the correct kind during instantiation.

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

Bug: v8:9231
Change-Id: Ieb050b09d1c19f2a5a3e59132a1864dadb06775d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1630685
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61993}
This commit is contained in:
Thibaud Michaud 2019-06-05 10:53:37 +02:00 committed by Commit Bot
parent fe9d6a49d1
commit b15b2c9104
10 changed files with 256 additions and 125 deletions

View File

@ -6103,10 +6103,9 @@ wasm::WasmOpcode GetMathIntrinsicOpcode(WasmImportCallKind kind,
#undef CASE #undef CASE
} }
wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine, wasm::WasmCompilationResult CompileWasmMathIntrinsic(
wasm::NativeModule* native_module, wasm::WasmEngine* wasm_engine, WasmImportCallKind kind,
WasmImportCallKind kind, wasm::FunctionSig* sig) {
wasm::FunctionSig* sig) {
DCHECK_EQ(1, sig->return_count()); DCHECK_EQ(1, sig->return_count());
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
@ -6125,7 +6124,7 @@ wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
InstructionSelector::AlignmentRequirements())); InstructionSelector::AlignmentRequirements()));
wasm::CompilationEnv env( wasm::CompilationEnv env(
native_module->module(), wasm::UseTrapHandler::kNoTrapHandler, nullptr, wasm::UseTrapHandler::kNoTrapHandler,
wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport, wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport,
wasm::kAllWasmFeatures, wasm::LowerSimd::kNoLowerSimd); wasm::kAllWasmFeatures, wasm::LowerSimd::kNoLowerSimd);
@ -6167,21 +6166,12 @@ wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
wasm_engine, call_descriptor, mcgraph, Code::WASM_FUNCTION, wasm_engine, call_descriptor, mcgraph, Code::WASM_FUNCTION,
wasm::WasmCode::kFunction, debug_name, WasmStubAssemblerOptions(), wasm::WasmCode::kFunction, debug_name, WasmStubAssemblerOptions(),
source_positions); source_positions);
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode( return result;
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kFunction,
wasm::ExecutionTier::kNone);
// TODO(titzer): add counters for math intrinsic code size / allocation
return native_module->PublishCode(std::move(wasm_code));
} }
wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine, wasm::WasmCompilationResult CompileWasmImportCallWrapper(
wasm::NativeModule* native_module, wasm::WasmEngine* wasm_engine, wasm::CompilationEnv* env,
WasmImportCallKind kind, WasmImportCallKind kind, wasm::FunctionSig* sig, bool source_positions) {
wasm::FunctionSig* sig,
bool source_positions) {
DCHECK_NE(WasmImportCallKind::kLinkError, kind); DCHECK_NE(WasmImportCallKind::kLinkError, kind);
DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind); DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
@ -6189,7 +6179,7 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
if (FLAG_wasm_math_intrinsics && if (FLAG_wasm_math_intrinsics &&
kind >= WasmImportCallKind::kFirstMathIntrinsic && kind >= WasmImportCallKind::kFirstMathIntrinsic &&
kind <= WasmImportCallKind::kLastMathIntrinsic) { kind <= WasmImportCallKind::kLastMathIntrinsic) {
return CompileWasmMathIntrinsic(wasm_engine, native_module, kind, sig); return CompileWasmMathIntrinsic(wasm_engine, kind, sig);
} }
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
@ -6214,7 +6204,7 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, source_position_table, WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, source_position_table,
StubCallMode::kCallWasmRuntimeStub, StubCallMode::kCallWasmRuntimeStub,
native_module->enabled_features()); env->enabled_features);
builder.set_control_ptr(&control); builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect); builder.set_effect_ptr(&effect);
builder.BuildWasmImportCallWrapper(kind); builder.BuildWasmImportCallWrapper(kind);
@ -6232,13 +6222,8 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
wasm_engine, incoming, &jsgraph, Code::WASM_TO_JS_FUNCTION, wasm_engine, incoming, &jsgraph, Code::WASM_TO_JS_FUNCTION,
wasm::WasmCode::kWasmToJsWrapper, func_name, WasmStubAssemblerOptions(), wasm::WasmCode::kWasmToJsWrapper, func_name, WasmStubAssemblerOptions(),
source_position_table); source_position_table);
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode( result.kind = wasm::WasmCompilationResult::kWasmToJsWrapper;
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc, return result;
result.frame_slot_count, result.tagged_parameter_slots,
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kWasmToJsWrapper,
wasm::ExecutionTier::kNone);
return native_module->PublishCode(std::move(wasm_code));
} }
wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine, wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine,
@ -6290,9 +6275,8 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine,
wasm::WasmCode::kWasmToCapiWrapper, debug_name, wasm::WasmCode::kWasmToCapiWrapper, debug_name,
WasmStubAssemblerOptions(), source_positions); WasmStubAssemblerOptions(), source_positions);
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode( std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc, wasm::kAnonymousFuncIndex, result.code_desc, result.frame_slot_count,
result.frame_slot_count, result.tagged_parameter_slots, result.tagged_parameter_slots, std::move(result.protected_instructions),
std::move(result.protected_instructions),
std::move(result.source_positions), wasm::WasmCode::kWasmToCapiWrapper, std::move(result.source_positions), wasm::WasmCode::kWasmToCapiWrapper,
wasm::ExecutionTier::kNone); wasm::ExecutionTier::kNone);
return native_module->PublishCode(std::move(wasm_code)); return native_module->PublishCode(std::move(wasm_code));
@ -6338,6 +6322,7 @@ wasm::WasmCompilationResult CompileWasmInterpreterEntry(
wasm::WasmCode::kInterpreterEntry, func_name.begin(), wasm::WasmCode::kInterpreterEntry, func_name.begin(),
WasmStubAssemblerOptions()); WasmStubAssemblerOptions());
result.result_tier = wasm::ExecutionTier::kInterpreter; result.result_tier = wasm::ExecutionTier::kInterpreter;
result.kind = wasm::WasmCompilationResult::kInterpreterEntry;
return result; return result;
} }

View File

@ -103,13 +103,19 @@ enum class WasmImportCallKind : uint8_t {
kUseCallBuiltin kUseCallBuiltin
}; };
// TODO(wasm): There should be only one import kind for sloppy and strict in
// order to reduce wrapper cache misses. The mode can be checked at runtime
// instead.
constexpr WasmImportCallKind kDefaultImportCallKind =
WasmImportCallKind::kJSFunctionArityMatchSloppy;
V8_EXPORT_PRIVATE WasmImportCallKind V8_EXPORT_PRIVATE WasmImportCallKind
GetWasmImportCallKind(Handle<JSReceiver> callable, wasm::FunctionSig* sig, GetWasmImportCallKind(Handle<JSReceiver> callable, wasm::FunctionSig* sig,
bool has_bigint_feature); bool has_bigint_feature);
// Compiles an import call wrapper, which allows WASM to call imports. // Compiles an import call wrapper, which allows WASM to call imports.
V8_EXPORT_PRIVATE wasm::WasmCode* CompileWasmImportCallWrapper( V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmImportCallWrapper(
wasm::WasmEngine*, wasm::NativeModule*, WasmImportCallKind, wasm::WasmEngine*, wasm::CompilationEnv* env, WasmImportCallKind,
wasm::FunctionSig*, bool source_positions); wasm::FunctionSig*, bool source_positions);
// Compiles a host call wrapper, which allows WASM to call host functions. // Compiles a host call wrapper, which allows WASM to call host functions.

View File

@ -113,6 +113,42 @@ ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier(
} }
WasmCompilationResult WasmCompilationUnit::ExecuteCompilation( WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
WasmEngine* engine, CompilationEnv* env,
const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
Counters* counters, WasmFeatures* detected) {
WasmCompilationResult result;
if (func_index_ < static_cast<int>(env->module->num_imported_functions)) {
result = ExecuteImportWrapperCompilation(engine, env);
} else {
result = ExecuteFunctionCompilation(engine, env, wire_bytes_storage,
counters, detected);
}
if (result.succeeded()) {
counters->wasm_generated_code_size()->Increment(
result.code_desc.instr_size);
counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
}
result.func_index = func_index_;
result.requested_tier = tier_;
return result;
}
WasmCompilationResult WasmCompilationUnit::ExecuteImportWrapperCompilation(
WasmEngine* engine, CompilationEnv* env) {
FunctionSig* sig = env->module->functions[func_index_].sig;
// Assume the wrapper is going to be a JS function with matching arity at
// instantiation time.
auto kind = compiler::kDefaultImportCallKind;
bool source_positions = env->module->origin == kAsmJsOrigin;
WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
engine, env, kind, sig, source_positions);
return result;
}
WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation(
WasmEngine* wasm_engine, CompilationEnv* env, WasmEngine* wasm_engine, CompilationEnv* env,
const std::shared_ptr<WireBytesStorage>& wire_bytes_storage, const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
Counters* counters, WasmFeatures* detected) { Counters* counters, WasmFeatures* detected) {
@ -167,15 +203,6 @@ WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
break; break;
} }
result.func_index = func_index_;
result.requested_tier = tier_;
if (result.succeeded()) {
counters->wasm_generated_code_size()->Increment(
result.code_desc.instr_size);
counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
}
return result; return result;
} }
@ -190,6 +217,8 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
wire_bytes.start() + function->code.offset(), wire_bytes.start() + function->code.offset(),
wire_bytes.start() + function->code.end_offset()}; wire_bytes.start() + function->code.end_offset()};
DCHECK_LE(native_module->num_imported_functions(), function->func_index);
DCHECK_LT(function->func_index, native_module->num_functions());
WasmCompilationUnit unit(function->func_index, tier); WasmCompilationUnit unit(function->func_index, tier);
CompilationEnv env = native_module->CreateCompilationEnv(); CompilationEnv env = native_module->CreateCompilationEnv();
WasmCompilationResult result = unit.ExecuteCompilation( WasmCompilationResult result = unit.ExecuteCompilation(

View File

@ -43,6 +43,12 @@ struct WasmCompilationResult {
public: public:
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmCompilationResult); MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmCompilationResult);
enum Kind : int8_t {
kFunction,
kWasmToJsWrapper,
kInterpreterEntry,
};
bool succeeded() const { return code_desc.buffer != nullptr; } bool succeeded() const { return code_desc.buffer != nullptr; }
bool failed() const { return !succeeded(); } bool failed() const { return !succeeded(); }
operator bool() const { return succeeded(); } operator bool() const { return succeeded(); }
@ -53,9 +59,10 @@ struct WasmCompilationResult {
uint32_t tagged_parameter_slots = 0; uint32_t tagged_parameter_slots = 0;
OwnedVector<byte> source_positions; OwnedVector<byte> source_positions;
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions; OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions;
int func_index; int func_index = static_cast<int>(kAnonymousFuncIndex);
ExecutionTier requested_tier; ExecutionTier requested_tier;
ExecutionTier result_tier; ExecutionTier result_tier;
Kind kind = kFunction;
}; };
class V8_EXPORT_PRIVATE WasmCompilationUnit final { class V8_EXPORT_PRIVATE WasmCompilationUnit final {
@ -77,6 +84,14 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final {
ExecutionTier); ExecutionTier);
private: private:
WasmCompilationResult ExecuteFunctionCompilation(
WasmEngine* wasm_engine, CompilationEnv* env,
const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
Counters* counters, WasmFeatures* detected);
WasmCompilationResult ExecuteImportWrapperCompilation(WasmEngine* engine,
CompilationEnv* env);
int func_index_; int func_index_;
ExecutionTier tier_; ExecutionTier tier_;
}; };

View File

@ -33,6 +33,7 @@
#include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-memory.h" #include "src/wasm/wasm-memory.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-result.h" #include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-serialization.h" #include "src/wasm/wasm-serialization.h"
@ -380,7 +381,7 @@ class CompilationStateImpl {
// Initialize compilation progress. Set compilation tiers to expect for // Initialize compilation progress. Set compilation tiers to expect for
// baseline and top tier compilation. Must be set before {AddCompilationUnits} // baseline and top tier compilation. Must be set before {AddCompilationUnits}
// is invoked which triggers background compilation. // is invoked which triggers background compilation.
void InitializeCompilationProgress(bool lazy_module); void InitializeCompilationProgress(bool lazy_module, int num_import_wrappers);
// Add the callback function to be called on compilation events. Needs to be // Add the callback function to be called on compilation events. Needs to be
// set before {AddCompilationUnits} is run to ensure that it receives all // set before {AddCompilationUnits} is run to ensure that it receives all
@ -409,13 +410,11 @@ class CompilationStateImpl {
bool baseline_compilation_finished() const { bool baseline_compilation_finished() const {
base::MutexGuard guard(&callbacks_mutex_); base::MutexGuard guard(&callbacks_mutex_);
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_); return outstanding_baseline_units_ == 0;
return outstanding_baseline_functions_ == 0;
} }
bool top_tier_compilation_finished() const { bool top_tier_compilation_finished() const {
base::MutexGuard guard(&callbacks_mutex_); base::MutexGuard guard(&callbacks_mutex_);
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
return outstanding_top_tier_functions_ == 0; return outstanding_top_tier_functions_ == 0;
} }
@ -517,7 +516,7 @@ class CompilationStateImpl {
// Callback functions to be called on compilation events. // Callback functions to be called on compilation events.
std::vector<CompilationState::callback_t> callbacks_; std::vector<CompilationState::callback_t> callbacks_;
int outstanding_baseline_functions_ = 0; int outstanding_baseline_units_ = 0;
int outstanding_top_tier_functions_ = 0; int outstanding_top_tier_functions_ = 0;
std::vector<uint8_t> compilation_progress_; std::vector<uint8_t> compilation_progress_;
@ -699,6 +698,10 @@ class CompilationUnitBuilder {
native_module->module())) {} native_module->module())) {}
void AddUnits(uint32_t func_index) { void AddUnits(uint32_t func_index) {
if (func_index < native_module_->module()->num_imported_functions) {
baseline_units_.emplace_back(func_index, ExecutionTier::kNone);
return;
}
ExecutionTierPair tiers = GetRequestedExecutionTiers( ExecutionTierPair tiers = GetRequestedExecutionTiers(
native_module_->module(), compilation_state()->compile_mode(), native_module_->module(), compilation_state()->compile_mode(),
native_module_->enabled_features(), func_index); native_module_->enabled_features(), func_index);
@ -846,6 +849,8 @@ bool CompileLazy(Isolate* isolate, NativeModule* native_module,
ExecutionTierPair tiers = GetRequestedExecutionTiers( ExecutionTierPair tiers = GetRequestedExecutionTiers(
module, compilation_state->compile_mode(), enabled_features, func_index); module, compilation_state->compile_mode(), enabled_features, func_index);
DCHECK_LE(native_module->num_imported_functions(), func_index);
DCHECK_LT(func_index, native_module->num_functions());
WasmCompilationUnit baseline_unit(func_index, tiers.baseline_tier); WasmCompilationUnit baseline_unit(func_index, tiers.baseline_tier);
CompilationEnv env = native_module->CreateCompilationEnv(); CompilationEnv env = native_module->CreateCompilationEnv();
WasmCompilationResult result = baseline_unit.ExecuteCompilation( WasmCompilationResult result = baseline_unit.ExecuteCompilation(
@ -970,6 +975,29 @@ bool ExecuteCompilationUnits(
std::vector<WasmCode*> code_vector = std::vector<WasmCode*> code_vector =
compile_scope->native_module()->AddCompiledCode( compile_scope->native_module()->AddCompiledCode(
VectorOf(results_to_publish)); VectorOf(results_to_publish));
// For import wrapper compilation units, add result to the cache.
const NativeModule* native_module = compile_scope->native_module();
int num_imported_functions = native_module->num_imported_functions();
DCHECK_EQ(code_vector.size(), results_to_publish.size());
WasmImportWrapperCache* cache = native_module->import_wrapper_cache();
for (WasmCode* code : code_vector) {
int func_index = code->index();
DCHECK_LE(0, func_index);
DCHECK_LT(func_index, native_module->num_functions());
if (func_index < num_imported_functions) {
FunctionSig* sig = native_module->module()->functions[func_index].sig;
WasmImportWrapperCache::CacheKey key(compiler::kDefaultImportCallKind,
sig);
// If two imported functions have the same key, only one of them should
// have been added as a compilation unit. So it is always the first time
// we compile a wrapper for this key here.
DCHECK_NULL((*cache)[key]);
(*cache)[key] = code;
code->IncRef();
}
}
compile_scope->compilation_state()->OnFinishedUnits(VectorOf(code_vector)); compile_scope->compilation_state()->OnFinishedUnits(VectorOf(code_vector));
results_to_publish.clear(); results_to_publish.clear();
}; };
@ -1021,15 +1049,41 @@ bool ExecuteCompilationUnits(
return true; return true;
} }
namespace {
// Returns the number of units added.
int AddImportWrapperUnits(NativeModule* native_module,
CompilationUnitBuilder* builder) {
std::unordered_set<WasmImportWrapperCache::CacheKey,
WasmImportWrapperCache::CacheKeyHash>
keys;
int num_imported_functions = native_module->num_imported_functions();
for (int func_index = 0; func_index < num_imported_functions; func_index++) {
FunctionSig* sig = native_module->module()->functions[func_index].sig;
bool has_bigint_feature = native_module->enabled_features().bigint;
if (!IsJSCompatibleSignature(sig, has_bigint_feature)) {
continue;
}
WasmImportWrapperCache::CacheKey key(compiler::kDefaultImportCallKind, sig);
auto it = keys.insert(key);
if (it.second) {
// Ensure that all keys exist in the cache, so that we can populate the
// cache later without locking.
(*native_module->import_wrapper_cache())[key] = nullptr;
builder->AddUnits(func_index);
}
}
return static_cast<int>(keys.size());
}
} // namespace
void InitializeCompilationUnits(NativeModule* native_module) { void InitializeCompilationUnits(NativeModule* native_module) {
CompilationStateImpl* compilation_state = CompilationStateImpl* compilation_state =
Impl(native_module->compilation_state()); Impl(native_module->compilation_state());
const bool lazy_module = IsLazyModule(native_module->module()); const bool lazy_module = IsLazyModule(native_module->module());
compilation_state->InitializeCompilationProgress(lazy_module);
ModuleWireBytes wire_bytes(native_module->wire_bytes()); ModuleWireBytes wire_bytes(native_module->wire_bytes());
CompilationUnitBuilder builder(native_module); CompilationUnitBuilder builder(native_module);
auto* module = native_module->module(); auto* module = native_module->module();
uint32_t start = module->num_imported_functions; uint32_t start = module->num_imported_functions;
uint32_t end = start + module->num_declared_functions; uint32_t end = start + module->num_declared_functions;
for (uint32_t func_index = start; func_index < end; func_index++) { for (uint32_t func_index = start; func_index < end; func_index++) {
@ -1045,6 +1099,9 @@ void InitializeCompilationUnits(NativeModule* native_module) {
builder.AddUnits(func_index); builder.AddUnits(func_index);
} }
} }
int num_import_wrappers = AddImportWrapperUnits(native_module, &builder);
compilation_state->InitializeCompilationProgress(lazy_module,
num_import_wrappers);
builder.Commit(); builder.Commit();
} }
@ -1850,13 +1907,19 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage)); compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
DCHECK_EQ(job_->native_module_->module()->origin, kWasmOrigin); DCHECK_EQ(job_->native_module_->module()->origin, kWasmOrigin);
const bool lazy_module = job_->wasm_lazy_compilation_; const bool lazy_module = job_->wasm_lazy_compilation_;
compilation_state->InitializeCompilationProgress(lazy_module);
// Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the // Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
// AsyncStreamingProcessor have to finish. // AsyncStreamingProcessor have to finish.
job_->outstanding_finishers_.store(2); job_->outstanding_finishers_.store(2);
compilation_unit_builder_.reset( compilation_unit_builder_.reset(
new CompilationUnitBuilder(job_->native_module_.get())); new CompilationUnitBuilder(job_->native_module_.get()));
NativeModule* native_module = job_->native_module_.get();
int num_import_wrappers =
AddImportWrapperUnits(native_module, compilation_unit_builder_.get());
compilation_state->InitializeCompilationProgress(lazy_module,
num_import_wrappers);
return true; return true;
} }
@ -2022,16 +2085,16 @@ void CompilationStateImpl::AbortCompilation() {
callbacks_.clear(); callbacks_.clear();
} }
void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) { void CompilationStateImpl::InitializeCompilationProgress(
bool lazy_module, int num_import_wrappers) {
DCHECK(!failed()); DCHECK(!failed());
auto enabled_features = native_module_->enabled_features(); auto enabled_features = native_module_->enabled_features();
auto* module = native_module_->module(); auto* module = native_module_->module();
base::MutexGuard guard(&callbacks_mutex_); base::MutexGuard guard(&callbacks_mutex_);
DCHECK_EQ(0, outstanding_baseline_functions_); DCHECK_EQ(0, outstanding_baseline_units_);
DCHECK_EQ(0, outstanding_top_tier_functions_); DCHECK_EQ(0, outstanding_top_tier_functions_);
compilation_progress_.reserve(module->num_declared_functions); compilation_progress_.reserve(module->num_declared_functions);
int start = module->num_imported_functions; int start = module->num_imported_functions;
int end = start + module->num_declared_functions; int end = start + module->num_declared_functions;
for (int func_index = start; func_index < end; func_index++) { for (int func_index = start; func_index < end; func_index++) {
@ -2047,7 +2110,7 @@ void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) {
strategy == CompileStrategy::kLazyBaselineEagerTopTier); strategy == CompileStrategy::kLazyBaselineEagerTopTier);
// Count functions to complete baseline and top tier compilation. // Count functions to complete baseline and top tier compilation.
if (required_for_baseline) outstanding_baseline_functions_++; if (required_for_baseline) outstanding_baseline_units_++;
if (required_for_top_tier) outstanding_top_tier_functions_++; if (required_for_top_tier) outstanding_top_tier_functions_++;
// Initialize function's compilation progress. // Initialize function's compilation progress.
@ -2063,24 +2126,25 @@ void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) {
RequiredTopTierField::update(function_progress, required_top_tier); RequiredTopTierField::update(function_progress, required_top_tier);
compilation_progress_.push_back(function_progress); compilation_progress_.push_back(function_progress);
} }
DCHECK_IMPLIES(lazy_module, outstanding_baseline_functions_ == 0); DCHECK_IMPLIES(lazy_module, outstanding_baseline_units_ == 0);
DCHECK_IMPLIES(lazy_module, outstanding_top_tier_functions_ == 0); DCHECK_IMPLIES(lazy_module, outstanding_top_tier_functions_ == 0);
DCHECK_LE(0, outstanding_baseline_functions_); DCHECK_LE(0, outstanding_baseline_units_);
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_); DCHECK_LE(outstanding_baseline_units_, outstanding_top_tier_functions_);
outstanding_baseline_units_ += num_import_wrappers;
// Trigger callbacks if module needs no baseline or top tier compilation. This // Trigger callbacks if module needs no baseline or top tier compilation. This
// can be the case for an empty or fully lazy module. // can be the case for an empty or fully lazy module.
if (outstanding_baseline_functions_ == 0) { if (outstanding_baseline_units_ == 0) {
for (auto& callback : callbacks_) { for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedBaselineCompilation); callback(CompilationEvent::kFinishedBaselineCompilation);
} }
} if (outstanding_top_tier_functions_ == 0) {
if (outstanding_top_tier_functions_ == 0) { for (auto& callback : callbacks_) {
for (auto& callback : callbacks_) { callback(CompilationEvent::kFinishedTopTierCompilation);
callback(CompilationEvent::kFinishedTopTierCompilation); }
// Clear the callbacks because no more events will be delivered.
callbacks_.clear();
} }
// Clear the callbacks because no more events will be delivered.
callbacks_.clear();
} }
} }
@ -2113,10 +2177,10 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
base::MutexGuard guard(&callbacks_mutex_); base::MutexGuard guard(&callbacks_mutex_);
// In case of no outstanding functions we can return early. // In case of no outstanding compilation units we can return early.
// This is especially important for lazy modules that were deserialized. // This is especially important for lazy modules that were deserialized.
// Compilation progress was not set up in these cases. // Compilation progress was not set up in these cases.
if (outstanding_baseline_functions_ == 0 && if (outstanding_baseline_units_ == 0 &&
outstanding_top_tier_functions_ == 0) { outstanding_top_tier_functions_ == 0) {
return; return;
} }
@ -2133,49 +2197,61 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
for (WasmCode* code : code_vector) { for (WasmCode* code : code_vector) {
DCHECK_NOT_NULL(code); DCHECK_NOT_NULL(code);
DCHECK_NE(code->tier(), ExecutionTier::kNone); DCHECK_LT(code->index(), native_module_->num_functions());
native_module_->engine()->LogCode(code);
// Read function's compilation progress.
// This view on the compilation progress may differ from the actually
// compiled code. Any lazily compiled function does not contribute to the
// compilation progress but may publish code to the code manager.
int slot_index =
code->index() - native_module_->module()->num_imported_functions;
uint8_t function_progress = compilation_progress_[slot_index];
ExecutionTier required_baseline_tier =
RequiredBaselineTierField::decode(function_progress);
ExecutionTier required_top_tier =
RequiredTopTierField::decode(function_progress);
ExecutionTier reached_tier = ReachedTierField::decode(function_progress);
bool completes_baseline_compilation = false; bool completes_baseline_compilation = false;
bool completes_top_tier_compilation = false; bool completes_top_tier_compilation = false;
// Check whether required baseline or top tier are reached. if (code->index() < native_module_->num_imported_functions()) {
if (reached_tier < required_baseline_tier && // Import wrapper.
required_baseline_tier <= code->tier()) { DCHECK_EQ(code->tier(), ExecutionTier::kTurbofan);
DCHECK_GT(outstanding_baseline_functions_, 0); outstanding_baseline_units_--;
outstanding_baseline_functions_--; if (outstanding_baseline_units_ == 0) {
if (outstanding_baseline_functions_ == 0) {
completes_baseline_compilation = true; completes_baseline_compilation = true;
} }
} } else {
if (reached_tier < required_top_tier && required_top_tier <= code->tier()) { // Function.
DCHECK_GT(outstanding_top_tier_functions_, 0); DCHECK_NE(code->tier(), ExecutionTier::kNone);
outstanding_top_tier_functions_--; native_module_->engine()->LogCode(code);
if (outstanding_top_tier_functions_ == 0) {
completes_top_tier_compilation = true;
}
}
// Update function's compilation progress. // Read function's compilation progress.
if (code->tier() > reached_tier) { // This view on the compilation progress may differ from the actually
compilation_progress_[slot_index] = ReachedTierField::update( // compiled code. Any lazily compiled function does not contribute to the
compilation_progress_[slot_index], code->tier()); // compilation progress but may publish code to the code manager.
int slot_index =
code->index() - native_module_->module()->num_imported_functions;
uint8_t function_progress = compilation_progress_[slot_index];
ExecutionTier required_baseline_tier =
RequiredBaselineTierField::decode(function_progress);
ExecutionTier required_top_tier =
RequiredTopTierField::decode(function_progress);
ExecutionTier reached_tier = ReachedTierField::decode(function_progress);
// Check whether required baseline or top tier are reached.
if (reached_tier < required_baseline_tier &&
required_baseline_tier <= code->tier()) {
DCHECK_GT(outstanding_baseline_units_, 0);
outstanding_baseline_units_--;
if (outstanding_baseline_units_ == 0) {
completes_baseline_compilation = true;
}
}
if (reached_tier < required_top_tier &&
required_top_tier <= code->tier()) {
DCHECK_GT(outstanding_top_tier_functions_, 0);
outstanding_top_tier_functions_--;
if (outstanding_top_tier_functions_ == 0) {
completes_top_tier_compilation = true;
}
}
// Update function's compilation progress.
if (code->tier() > reached_tier) {
compilation_progress_[slot_index] = ReachedTierField::update(
compilation_progress_[slot_index], code->tier());
}
DCHECK_LE(0, outstanding_baseline_units_);
} }
DCHECK_LE(0, outstanding_baseline_functions_);
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
// Trigger callbacks. // Trigger callbacks.
if (completes_baseline_compilation) { if (completes_baseline_compilation) {
@ -2183,8 +2259,11 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
for (auto& callback : callbacks_) { for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedBaselineCompilation); callback(CompilationEvent::kFinishedBaselineCompilation);
} }
if (outstanding_top_tier_functions_ == 0) {
completes_top_tier_compilation = true;
}
} }
if (completes_top_tier_compilation) { if (outstanding_baseline_units_ == 0 && completes_top_tier_compilation) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "TopTierFinished"); TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "TopTierFinished");
for (auto& callback : callbacks_) { for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedTopTierCompilation); callback(CompilationEvent::kFinishedTopTierCompilation);
@ -2311,14 +2390,21 @@ WasmCode* CompileImportWrapper(
bool source_positions = native_module->module()->origin == kAsmJsOrigin; bool source_positions = native_module->module()->origin == kAsmJsOrigin;
// Keep the {WasmCode} alive until we explicitly call {IncRef}. // Keep the {WasmCode} alive until we explicitly call {IncRef}.
WasmCodeRefScope code_ref_scope; WasmCodeRefScope code_ref_scope;
WasmCode* wasm_code = compiler::CompileWasmImportCallWrapper( CompilationEnv env = native_module->CreateCompilationEnv();
wasm_engine, native_module, kind, sig, source_positions); WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
(*cache_scope)[key] = wasm_code; wasm_engine, &env, kind, sig, source_positions);
wasm_code->IncRef(); std::unique_ptr<WasmCode> wasm_code = native_module->AddCode(
result.func_index, result.code_desc, result.frame_slot_count,
result.tagged_parameter_slots, std::move(result.protected_instructions),
std::move(result.source_positions), GetCodeKind(result),
ExecutionTier::kNone);
WasmCode* published_code = native_module->PublishCode(std::move(wasm_code));
(*cache_scope)[key] = published_code;
published_code->IncRef();
counters->wasm_generated_code_size()->Increment( counters->wasm_generated_code_size()->Increment(
wasm_code->instructions().length()); published_code->instructions().length());
counters->wasm_reloc_size()->Increment(wasm_code->reloc_info().length()); counters->wasm_reloc_size()->Increment(published_code->reloc_info().length());
return wasm_code; return published_code;
} }
Handle<Script> CreateWasmScript(Isolate* isolate, Handle<Script> CreateWasmScript(Isolate* isolate,

View File

@ -822,7 +822,7 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code,
DCHECK_NE(kind, WasmCode::Kind::kInterpreterEntry); DCHECK_NE(kind, WasmCode::Kind::kInterpreterEntry);
std::unique_ptr<WasmCode> new_code{new WasmCode{ std::unique_ptr<WasmCode> new_code{new WasmCode{
this, // native_module this, // native_module
WasmCode::kAnonymousFuncIndex, // index kAnonymousFuncIndex, // index
dst_code_bytes, // instructions dst_code_bytes, // instructions
stack_slots, // stack_slots stack_slots, // stack_slots
0, // tagged_parameter_slots 0, // tagged_parameter_slots
@ -928,27 +928,26 @@ WasmCode* NativeModule::PublishCode(std::unique_ptr<WasmCode> code) {
return PublishCodeLocked(std::move(code)); return PublishCodeLocked(std::move(code));
} }
namespace { WasmCode::Kind GetCodeKind(const WasmCompilationResult& result) {
WasmCode::Kind GetCodeKindForExecutionTier(ExecutionTier tier) { switch (result.kind) {
switch (tier) { case WasmCompilationResult::kWasmToJsWrapper:
case ExecutionTier::kInterpreter: return WasmCode::Kind::kWasmToJsWrapper;
case WasmCompilationResult::kInterpreterEntry:
return WasmCode::Kind::kInterpreterEntry; return WasmCode::Kind::kInterpreterEntry;
case ExecutionTier::kLiftoff: case WasmCompilationResult::kFunction:
case ExecutionTier::kTurbofan:
return WasmCode::Kind::kFunction; return WasmCode::Kind::kFunction;
case ExecutionTier::kNone: default:
UNREACHABLE(); UNREACHABLE();
} }
} }
} // namespace
WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) { WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) {
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here. // The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
DCHECK(!allocation_mutex_.TryLock()); DCHECK(!allocation_mutex_.TryLock());
if (!code->IsAnonymous()) { if (!code->IsAnonymous() &&
code->index() >= module_->num_imported_functions) {
DCHECK_LT(code->index(), num_functions()); DCHECK_LT(code->index(), num_functions());
DCHECK_LE(module_->num_imported_functions, code->index());
// Assume an order of execution tiers that represents the quality of their // Assume an order of execution tiers that represents the quality of their
// generated code. // generated code.
@ -1054,7 +1053,7 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) {
ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size()); ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size());
std::unique_ptr<WasmCode> code{new WasmCode{ std::unique_ptr<WasmCode> code{new WasmCode{
this, // native_module this, // native_module
WasmCode::kAnonymousFuncIndex, // index kAnonymousFuncIndex, // index
code_space, // instructions code_space, // instructions
0, // stack_slots 0, // stack_slots
0, // tagged_parameter_slots 0, // tagged_parameter_slots
@ -1414,9 +1413,8 @@ std::vector<WasmCode*> NativeModule::AddCompiledCode(
generated_code.emplace_back(AddCodeWithCodeSpace( generated_code.emplace_back(AddCodeWithCodeSpace(
result.func_index, result.code_desc, result.frame_slot_count, result.func_index, result.code_desc, result.frame_slot_count,
result.tagged_parameter_slots, std::move(result.protected_instructions), result.tagged_parameter_slots, std::move(result.protected_instructions),
std::move(result.source_positions), std::move(result.source_positions), GetCodeKind(result),
GetCodeKindForExecutionTier(result.result_tier), result.result_tier, result.result_tier, this_code_space));
this_code_space));
} }
DCHECK_EQ(0, code_space.size()); DCHECK_EQ(0, code_space.size());

View File

@ -176,7 +176,6 @@ class V8_EXPORT_PRIVATE WasmCode final {
enum FlushICache : bool { kFlushICache = true, kNoFlushICache = false }; enum FlushICache : bool { kFlushICache = true, kNoFlushICache = false };
static constexpr uint32_t kAnonymousFuncIndex = 0xffffffff;
STATIC_ASSERT(kAnonymousFuncIndex > kV8MaxWasmFunctions); STATIC_ASSERT(kAnonymousFuncIndex > kV8MaxWasmFunctions);
private: private:
@ -270,6 +269,8 @@ class V8_EXPORT_PRIVATE WasmCode final {
DISALLOW_COPY_AND_ASSIGN(WasmCode); DISALLOW_COPY_AND_ASSIGN(WasmCode);
}; };
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result);
// Return a textual description of the kind. // Return a textual description of the kind.
const char* GetWasmCodeKindAsString(WasmCode::Kind); const char* GetWasmCodeKindAsString(WasmCode::Kind);

View File

@ -106,6 +106,8 @@ constexpr WasmCodePosition kNoCodePosition = -1;
constexpr uint32_t kExceptionAttribute = 0; constexpr uint32_t kExceptionAttribute = 0;
constexpr uint32_t kAnonymousFuncIndex = 0xffffffff;
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -18,6 +18,11 @@ WasmCode*& WasmImportWrapperCache::ModificationScope::operator[](
return cache_->entry_map_[key]; return cache_->entry_map_[key];
} }
WasmCode*& WasmImportWrapperCache::operator[](
const WasmImportWrapperCache::CacheKey& key) {
return entry_map_[key];
}
WasmCode* WasmImportWrapperCache::Get(compiler::WasmImportCallKind kind, WasmCode* WasmImportWrapperCache::Get(compiler::WasmImportCallKind kind,
FunctionSig* sig) const { FunctionSig* sig) const {
auto it = entry_map_.find({kind, sig}); auto it = entry_map_.find({kind, sig});

View File

@ -45,6 +45,10 @@ class WasmImportWrapperCache {
base::MutexGuard guard_; base::MutexGuard guard_;
}; };
// Not thread-safe, use ModificationScope to get exclusive write access to the
// cache.
V8_EXPORT_PRIVATE WasmCode*& operator[](const CacheKey& key);
// Assumes the key exists in the map. // Assumes the key exists in the map.
V8_EXPORT_PRIVATE WasmCode* Get(compiler::WasmImportCallKind kind, V8_EXPORT_PRIVATE WasmCode* Get(compiler::WasmImportCallKind kind,
FunctionSig* sig) const; FunctionSig* sig) const;