[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:
parent
fe9d6a49d1
commit
b15b2c9104
@ -6103,10 +6103,9 @@ wasm::WasmOpcode GetMathIntrinsicOpcode(WasmImportCallKind kind,
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
|
||||
wasm::NativeModule* native_module,
|
||||
WasmImportCallKind kind,
|
||||
wasm::FunctionSig* sig) {
|
||||
wasm::WasmCompilationResult CompileWasmMathIntrinsic(
|
||||
wasm::WasmEngine* wasm_engine, WasmImportCallKind kind,
|
||||
wasm::FunctionSig* sig) {
|
||||
DCHECK_EQ(1, sig->return_count());
|
||||
|
||||
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
|
||||
@ -6125,7 +6124,7 @@ wasm::WasmCode* CompileWasmMathIntrinsic(wasm::WasmEngine* wasm_engine,
|
||||
InstructionSelector::AlignmentRequirements()));
|
||||
|
||||
wasm::CompilationEnv env(
|
||||
native_module->module(), wasm::UseTrapHandler::kNoTrapHandler,
|
||||
nullptr, wasm::UseTrapHandler::kNoTrapHandler,
|
||||
wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport,
|
||||
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::WasmCode::kFunction, debug_name, WasmStubAssemblerOptions(),
|
||||
source_positions);
|
||||
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
|
||||
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));
|
||||
return result;
|
||||
}
|
||||
|
||||
wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
wasm::NativeModule* native_module,
|
||||
WasmImportCallKind kind,
|
||||
wasm::FunctionSig* sig,
|
||||
bool source_positions) {
|
||||
wasm::WasmCompilationResult CompileWasmImportCallWrapper(
|
||||
wasm::WasmEngine* wasm_engine, wasm::CompilationEnv* env,
|
||||
WasmImportCallKind kind, wasm::FunctionSig* sig, bool source_positions) {
|
||||
DCHECK_NE(WasmImportCallKind::kLinkError, kind);
|
||||
DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
|
||||
|
||||
@ -6189,7 +6179,7 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
if (FLAG_wasm_math_intrinsics &&
|
||||
kind >= WasmImportCallKind::kFirstMathIntrinsic &&
|
||||
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"),
|
||||
@ -6214,7 +6204,7 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
|
||||
WasmWrapperGraphBuilder builder(&zone, &jsgraph, sig, source_position_table,
|
||||
StubCallMode::kCallWasmRuntimeStub,
|
||||
native_module->enabled_features());
|
||||
env->enabled_features);
|
||||
builder.set_control_ptr(&control);
|
||||
builder.set_effect_ptr(&effect);
|
||||
builder.BuildWasmImportCallWrapper(kind);
|
||||
@ -6232,13 +6222,8 @@ wasm::WasmCode* CompileWasmImportCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
wasm_engine, incoming, &jsgraph, Code::WASM_TO_JS_FUNCTION,
|
||||
wasm::WasmCode::kWasmToJsWrapper, func_name, WasmStubAssemblerOptions(),
|
||||
source_position_table);
|
||||
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
|
||||
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::kWasmToJsWrapper,
|
||||
wasm::ExecutionTier::kNone);
|
||||
return native_module->PublishCode(std::move(wasm_code));
|
||||
result.kind = wasm::WasmCompilationResult::kWasmToJsWrapper;
|
||||
return result;
|
||||
}
|
||||
|
||||
wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
@ -6290,9 +6275,8 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::WasmEngine* wasm_engine,
|
||||
wasm::WasmCode::kWasmToCapiWrapper, debug_name,
|
||||
WasmStubAssemblerOptions(), source_positions);
|
||||
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
|
||||
wasm::WasmCode::kAnonymousFuncIndex, result.code_desc,
|
||||
result.frame_slot_count, result.tagged_parameter_slots,
|
||||
std::move(result.protected_instructions),
|
||||
wasm::kAnonymousFuncIndex, result.code_desc, result.frame_slot_count,
|
||||
result.tagged_parameter_slots, std::move(result.protected_instructions),
|
||||
std::move(result.source_positions), wasm::WasmCode::kWasmToCapiWrapper,
|
||||
wasm::ExecutionTier::kNone);
|
||||
return native_module->PublishCode(std::move(wasm_code));
|
||||
@ -6338,6 +6322,7 @@ wasm::WasmCompilationResult CompileWasmInterpreterEntry(
|
||||
wasm::WasmCode::kInterpreterEntry, func_name.begin(),
|
||||
WasmStubAssemblerOptions());
|
||||
result.result_tier = wasm::ExecutionTier::kInterpreter;
|
||||
result.kind = wasm::WasmCompilationResult::kInterpreterEntry;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -103,13 +103,19 @@ enum class WasmImportCallKind : uint8_t {
|
||||
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
|
||||
GetWasmImportCallKind(Handle<JSReceiver> callable, wasm::FunctionSig* sig,
|
||||
bool has_bigint_feature);
|
||||
|
||||
// Compiles an import call wrapper, which allows WASM to call imports.
|
||||
V8_EXPORT_PRIVATE wasm::WasmCode* CompileWasmImportCallWrapper(
|
||||
wasm::WasmEngine*, wasm::NativeModule*, WasmImportCallKind,
|
||||
V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmImportCallWrapper(
|
||||
wasm::WasmEngine*, wasm::CompilationEnv* env, WasmImportCallKind,
|
||||
wasm::FunctionSig*, bool source_positions);
|
||||
|
||||
// Compiles a host call wrapper, which allows WASM to call host functions.
|
||||
|
@ -113,6 +113,42 @@ ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier(
|
||||
}
|
||||
|
||||
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,
|
||||
const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
|
||||
Counters* counters, WasmFeatures* detected) {
|
||||
@ -167,15 +203,6 @@ WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
|
||||
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;
|
||||
}
|
||||
|
||||
@ -190,6 +217,8 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
|
||||
wire_bytes.start() + function->code.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);
|
||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||
WasmCompilationResult result = unit.ExecuteCompilation(
|
||||
|
@ -43,6 +43,12 @@ struct WasmCompilationResult {
|
||||
public:
|
||||
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmCompilationResult);
|
||||
|
||||
enum Kind : int8_t {
|
||||
kFunction,
|
||||
kWasmToJsWrapper,
|
||||
kInterpreterEntry,
|
||||
};
|
||||
|
||||
bool succeeded() const { return code_desc.buffer != nullptr; }
|
||||
bool failed() const { return !succeeded(); }
|
||||
operator bool() const { return succeeded(); }
|
||||
@ -53,9 +59,10 @@ struct WasmCompilationResult {
|
||||
uint32_t tagged_parameter_slots = 0;
|
||||
OwnedVector<byte> source_positions;
|
||||
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions;
|
||||
int func_index;
|
||||
int func_index = static_cast<int>(kAnonymousFuncIndex);
|
||||
ExecutionTier requested_tier;
|
||||
ExecutionTier result_tier;
|
||||
Kind kind = kFunction;
|
||||
};
|
||||
|
||||
class V8_EXPORT_PRIVATE WasmCompilationUnit final {
|
||||
@ -77,6 +84,14 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final {
|
||||
ExecutionTier);
|
||||
|
||||
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_;
|
||||
ExecutionTier tier_;
|
||||
};
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "src/wasm/wasm-limits.h"
|
||||
#include "src/wasm/wasm-memory.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
#include "src/wasm/wasm-serialization.h"
|
||||
|
||||
@ -380,7 +381,7 @@ class CompilationStateImpl {
|
||||
// Initialize compilation progress. Set compilation tiers to expect for
|
||||
// baseline and top tier compilation. Must be set before {AddCompilationUnits}
|
||||
// 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
|
||||
// set before {AddCompilationUnits} is run to ensure that it receives all
|
||||
@ -409,13 +410,11 @@ class CompilationStateImpl {
|
||||
|
||||
bool baseline_compilation_finished() const {
|
||||
base::MutexGuard guard(&callbacks_mutex_);
|
||||
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
|
||||
return outstanding_baseline_functions_ == 0;
|
||||
return outstanding_baseline_units_ == 0;
|
||||
}
|
||||
|
||||
bool top_tier_compilation_finished() const {
|
||||
base::MutexGuard guard(&callbacks_mutex_);
|
||||
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
|
||||
return outstanding_top_tier_functions_ == 0;
|
||||
}
|
||||
|
||||
@ -517,7 +516,7 @@ class CompilationStateImpl {
|
||||
// Callback functions to be called on compilation events.
|
||||
std::vector<CompilationState::callback_t> callbacks_;
|
||||
|
||||
int outstanding_baseline_functions_ = 0;
|
||||
int outstanding_baseline_units_ = 0;
|
||||
int outstanding_top_tier_functions_ = 0;
|
||||
std::vector<uint8_t> compilation_progress_;
|
||||
|
||||
@ -699,6 +698,10 @@ class CompilationUnitBuilder {
|
||||
native_module->module())) {}
|
||||
|
||||
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(
|
||||
native_module_->module(), compilation_state()->compile_mode(),
|
||||
native_module_->enabled_features(), func_index);
|
||||
@ -846,6 +849,8 @@ bool CompileLazy(Isolate* isolate, NativeModule* native_module,
|
||||
ExecutionTierPair tiers = GetRequestedExecutionTiers(
|
||||
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);
|
||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||
WasmCompilationResult result = baseline_unit.ExecuteCompilation(
|
||||
@ -970,6 +975,29 @@ bool ExecuteCompilationUnits(
|
||||
std::vector<WasmCode*> code_vector =
|
||||
compile_scope->native_module()->AddCompiledCode(
|
||||
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));
|
||||
results_to_publish.clear();
|
||||
};
|
||||
@ -1021,15 +1049,41 @@ bool ExecuteCompilationUnits(
|
||||
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) {
|
||||
CompilationStateImpl* compilation_state =
|
||||
Impl(native_module->compilation_state());
|
||||
const bool lazy_module = IsLazyModule(native_module->module());
|
||||
compilation_state->InitializeCompilationProgress(lazy_module);
|
||||
|
||||
ModuleWireBytes wire_bytes(native_module->wire_bytes());
|
||||
CompilationUnitBuilder builder(native_module);
|
||||
auto* module = native_module->module();
|
||||
|
||||
uint32_t start = module->num_imported_functions;
|
||||
uint32_t end = start + module->num_declared_functions;
|
||||
for (uint32_t func_index = start; func_index < end; func_index++) {
|
||||
@ -1045,6 +1099,9 @@ void InitializeCompilationUnits(NativeModule* native_module) {
|
||||
builder.AddUnits(func_index);
|
||||
}
|
||||
}
|
||||
int num_import_wrappers = AddImportWrapperUnits(native_module, &builder);
|
||||
compilation_state->InitializeCompilationProgress(lazy_module,
|
||||
num_import_wrappers);
|
||||
builder.Commit();
|
||||
}
|
||||
|
||||
@ -1850,13 +1907,19 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader(
|
||||
compilation_state->SetWireBytesStorage(std::move(wire_bytes_storage));
|
||||
DCHECK_EQ(job_->native_module_->module()->origin, kWasmOrigin);
|
||||
const bool lazy_module = job_->wasm_lazy_compilation_;
|
||||
compilation_state->InitializeCompilationProgress(lazy_module);
|
||||
|
||||
// Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
|
||||
// AsyncStreamingProcessor have to finish.
|
||||
job_->outstanding_finishers_.store(2);
|
||||
compilation_unit_builder_.reset(
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2022,16 +2085,16 @@ void CompilationStateImpl::AbortCompilation() {
|
||||
callbacks_.clear();
|
||||
}
|
||||
|
||||
void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) {
|
||||
void CompilationStateImpl::InitializeCompilationProgress(
|
||||
bool lazy_module, int num_import_wrappers) {
|
||||
DCHECK(!failed());
|
||||
auto enabled_features = native_module_->enabled_features();
|
||||
auto* module = native_module_->module();
|
||||
|
||||
base::MutexGuard guard(&callbacks_mutex_);
|
||||
DCHECK_EQ(0, outstanding_baseline_functions_);
|
||||
DCHECK_EQ(0, outstanding_baseline_units_);
|
||||
DCHECK_EQ(0, outstanding_top_tier_functions_);
|
||||
compilation_progress_.reserve(module->num_declared_functions);
|
||||
|
||||
int start = module->num_imported_functions;
|
||||
int end = start + module->num_declared_functions;
|
||||
for (int func_index = start; func_index < end; func_index++) {
|
||||
@ -2047,7 +2110,7 @@ void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) {
|
||||
strategy == CompileStrategy::kLazyBaselineEagerTopTier);
|
||||
|
||||
// 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_++;
|
||||
|
||||
// Initialize function's compilation progress.
|
||||
@ -2063,24 +2126,25 @@ void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module) {
|
||||
RequiredTopTierField::update(function_progress, required_top_tier);
|
||||
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_LE(0, outstanding_baseline_functions_);
|
||||
DCHECK_LE(outstanding_baseline_functions_, outstanding_top_tier_functions_);
|
||||
DCHECK_LE(0, outstanding_baseline_units_);
|
||||
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
|
||||
// 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_) {
|
||||
callback(CompilationEvent::kFinishedBaselineCompilation);
|
||||
}
|
||||
}
|
||||
if (outstanding_top_tier_functions_ == 0) {
|
||||
for (auto& callback : callbacks_) {
|
||||
callback(CompilationEvent::kFinishedTopTierCompilation);
|
||||
if (outstanding_top_tier_functions_ == 0) {
|
||||
for (auto& callback : callbacks_) {
|
||||
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_);
|
||||
|
||||
// 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.
|
||||
// Compilation progress was not set up in these cases.
|
||||
if (outstanding_baseline_functions_ == 0 &&
|
||||
if (outstanding_baseline_units_ == 0 &&
|
||||
outstanding_top_tier_functions_ == 0) {
|
||||
return;
|
||||
}
|
||||
@ -2133,49 +2197,61 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
|
||||
|
||||
for (WasmCode* code : code_vector) {
|
||||
DCHECK_NOT_NULL(code);
|
||||
DCHECK_NE(code->tier(), ExecutionTier::kNone);
|
||||
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);
|
||||
DCHECK_LT(code->index(), native_module_->num_functions());
|
||||
|
||||
bool completes_baseline_compilation = false;
|
||||
bool completes_top_tier_compilation = false;
|
||||
|
||||
// Check whether required baseline or top tier are reached.
|
||||
if (reached_tier < required_baseline_tier &&
|
||||
required_baseline_tier <= code->tier()) {
|
||||
DCHECK_GT(outstanding_baseline_functions_, 0);
|
||||
outstanding_baseline_functions_--;
|
||||
if (outstanding_baseline_functions_ == 0) {
|
||||
if (code->index() < native_module_->num_imported_functions()) {
|
||||
// Import wrapper.
|
||||
DCHECK_EQ(code->tier(), ExecutionTier::kTurbofan);
|
||||
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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Function.
|
||||
DCHECK_NE(code->tier(), ExecutionTier::kNone);
|
||||
native_module_->engine()->LogCode(code);
|
||||
|
||||
// Update function's compilation progress.
|
||||
if (code->tier() > reached_tier) {
|
||||
compilation_progress_[slot_index] = ReachedTierField::update(
|
||||
compilation_progress_[slot_index], code->tier());
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
if (completes_baseline_compilation) {
|
||||
@ -2183,8 +2259,11 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
|
||||
for (auto& callback : callbacks_) {
|
||||
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");
|
||||
for (auto& callback : callbacks_) {
|
||||
callback(CompilationEvent::kFinishedTopTierCompilation);
|
||||
@ -2311,14 +2390,21 @@ WasmCode* CompileImportWrapper(
|
||||
bool source_positions = native_module->module()->origin == kAsmJsOrigin;
|
||||
// Keep the {WasmCode} alive until we explicitly call {IncRef}.
|
||||
WasmCodeRefScope code_ref_scope;
|
||||
WasmCode* wasm_code = compiler::CompileWasmImportCallWrapper(
|
||||
wasm_engine, native_module, kind, sig, source_positions);
|
||||
(*cache_scope)[key] = wasm_code;
|
||||
wasm_code->IncRef();
|
||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||
WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
|
||||
wasm_engine, &env, kind, sig, source_positions);
|
||||
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(
|
||||
wasm_code->instructions().length());
|
||||
counters->wasm_reloc_size()->Increment(wasm_code->reloc_info().length());
|
||||
return wasm_code;
|
||||
published_code->instructions().length());
|
||||
counters->wasm_reloc_size()->Increment(published_code->reloc_info().length());
|
||||
return published_code;
|
||||
}
|
||||
|
||||
Handle<Script> CreateWasmScript(Isolate* isolate,
|
||||
|
@ -822,7 +822,7 @@ WasmCode* NativeModule::AddAndPublishAnonymousCode(Handle<Code> code,
|
||||
DCHECK_NE(kind, WasmCode::Kind::kInterpreterEntry);
|
||||
std::unique_ptr<WasmCode> new_code{new WasmCode{
|
||||
this, // native_module
|
||||
WasmCode::kAnonymousFuncIndex, // index
|
||||
kAnonymousFuncIndex, // index
|
||||
dst_code_bytes, // instructions
|
||||
stack_slots, // stack_slots
|
||||
0, // tagged_parameter_slots
|
||||
@ -928,27 +928,26 @@ WasmCode* NativeModule::PublishCode(std::unique_ptr<WasmCode> code) {
|
||||
return PublishCodeLocked(std::move(code));
|
||||
}
|
||||
|
||||
namespace {
|
||||
WasmCode::Kind GetCodeKindForExecutionTier(ExecutionTier tier) {
|
||||
switch (tier) {
|
||||
case ExecutionTier::kInterpreter:
|
||||
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result) {
|
||||
switch (result.kind) {
|
||||
case WasmCompilationResult::kWasmToJsWrapper:
|
||||
return WasmCode::Kind::kWasmToJsWrapper;
|
||||
case WasmCompilationResult::kInterpreterEntry:
|
||||
return WasmCode::Kind::kInterpreterEntry;
|
||||
case ExecutionTier::kLiftoff:
|
||||
case ExecutionTier::kTurbofan:
|
||||
case WasmCompilationResult::kFunction:
|
||||
return WasmCode::Kind::kFunction;
|
||||
case ExecutionTier::kNone:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
WasmCode* NativeModule::PublishCodeLocked(std::unique_ptr<WasmCode> code) {
|
||||
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
|
||||
DCHECK(!allocation_mutex_.TryLock());
|
||||
|
||||
if (!code->IsAnonymous()) {
|
||||
if (!code->IsAnonymous() &&
|
||||
code->index() >= module_->num_imported_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
|
||||
// generated code.
|
||||
@ -1054,7 +1053,7 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t jump_table_size) {
|
||||
ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size());
|
||||
std::unique_ptr<WasmCode> code{new WasmCode{
|
||||
this, // native_module
|
||||
WasmCode::kAnonymousFuncIndex, // index
|
||||
kAnonymousFuncIndex, // index
|
||||
code_space, // instructions
|
||||
0, // stack_slots
|
||||
0, // tagged_parameter_slots
|
||||
@ -1414,9 +1413,8 @@ std::vector<WasmCode*> NativeModule::AddCompiledCode(
|
||||
generated_code.emplace_back(AddCodeWithCodeSpace(
|
||||
result.func_index, result.code_desc, result.frame_slot_count,
|
||||
result.tagged_parameter_slots, std::move(result.protected_instructions),
|
||||
std::move(result.source_positions),
|
||||
GetCodeKindForExecutionTier(result.result_tier), result.result_tier,
|
||||
this_code_space));
|
||||
std::move(result.source_positions), GetCodeKind(result),
|
||||
result.result_tier, this_code_space));
|
||||
}
|
||||
DCHECK_EQ(0, code_space.size());
|
||||
|
||||
|
@ -176,7 +176,6 @@ class V8_EXPORT_PRIVATE WasmCode final {
|
||||
|
||||
enum FlushICache : bool { kFlushICache = true, kNoFlushICache = false };
|
||||
|
||||
static constexpr uint32_t kAnonymousFuncIndex = 0xffffffff;
|
||||
STATIC_ASSERT(kAnonymousFuncIndex > kV8MaxWasmFunctions);
|
||||
|
||||
private:
|
||||
@ -270,6 +269,8 @@ class V8_EXPORT_PRIVATE WasmCode final {
|
||||
DISALLOW_COPY_AND_ASSIGN(WasmCode);
|
||||
};
|
||||
|
||||
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result);
|
||||
|
||||
// Return a textual description of the kind.
|
||||
const char* GetWasmCodeKindAsString(WasmCode::Kind);
|
||||
|
||||
|
@ -106,6 +106,8 @@ constexpr WasmCodePosition kNoCodePosition = -1;
|
||||
|
||||
constexpr uint32_t kExceptionAttribute = 0;
|
||||
|
||||
constexpr uint32_t kAnonymousFuncIndex = 0xffffffff;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -18,6 +18,11 @@ WasmCode*& WasmImportWrapperCache::ModificationScope::operator[](
|
||||
return cache_->entry_map_[key];
|
||||
}
|
||||
|
||||
WasmCode*& WasmImportWrapperCache::operator[](
|
||||
const WasmImportWrapperCache::CacheKey& key) {
|
||||
return entry_map_[key];
|
||||
}
|
||||
|
||||
WasmCode* WasmImportWrapperCache::Get(compiler::WasmImportCallKind kind,
|
||||
FunctionSig* sig) const {
|
||||
auto it = entry_map_.find({kind, sig});
|
||||
|
@ -45,6 +45,10 @@ class WasmImportWrapperCache {
|
||||
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.
|
||||
V8_EXPORT_PRIVATE WasmCode* Get(compiler::WasmImportCallKind kind,
|
||||
FunctionSig* sig) const;
|
||||
|
Loading…
Reference in New Issue
Block a user