Revert "[wasm] Compile debug code lazily"
This reverts commit 7b138dd30d
.
Reason for revert: Causes multiple flakes:
https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Linux64%20TSAN%20-%20isolates/22932/overview
https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Linux%20-%20debug/41934/overview
Original change's description:
> [wasm] Compile debug code lazily
>
> Currently V8 recompiles all functions of a WebAssembly module when a
> debugging session starts. This is outdated behavior and
> causes OOMs for developers. With this CL all compiled code just gets
> removed when a debugging session starts, and debugging code gets
> compiled lazily.
>
> This behavior may lead to small delays whenever a new function gets
> entered by the debugger. However, developers are used to debugging code
> being slightly slower, and the small delays should be in the order of
> few milliseconds. On the other hand, debug modules can be big,
> sometimes even more than 1'000'000 functions, and developers reported
> OOMs when debugging.
>
> R=clemensb@chromium.org
>
> Bug: v8:13541, chromium:1372621, v8:13224
> Change-Id: Ia36d9b8743523b1c89221c59f989268e27f6ce98
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4067302
> Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
> Reviewed-by: Clemens Backes <clemensb@chromium.org>
> Commit-Queue: Andreas Haas <ahaas@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#84662}
Bug: v8:13541, chromium:1372621, v8:13224
Change-Id: Ic5442462d158618f2d43b8e0ebdfb90017ed378a
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4080034
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Michael Achenbach <machenbach@chromium.org>
Owners-Override: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84665}
This commit is contained in:
parent
3573da0bc8
commit
5073ba7d52
@ -8273,7 +8273,7 @@ wasm::WasmCode* CompileWasmCapiCallWrapper(wasm::NativeModule* native_module,
|
|||||||
result.tagged_parameter_slots,
|
result.tagged_parameter_slots,
|
||||||
result.protected_instructions_data.as_vector(),
|
result.protected_instructions_data.as_vector(),
|
||||||
result.source_positions.as_vector(), wasm::WasmCode::kWasmToCapiWrapper,
|
result.source_positions.as_vector(), wasm::WasmCode::kWasmToCapiWrapper,
|
||||||
wasm::ExecutionTier::kNone, wasm::kNotForDebugging);
|
wasm::ExecutionTier::kNone, wasm::kNoDebugging);
|
||||||
published_code = native_module->PublishCode(std::move(wasm_code));
|
published_code = native_module->PublishCode(std::move(wasm_code));
|
||||||
}
|
}
|
||||||
return published_code;
|
return published_code;
|
||||||
@ -8326,7 +8326,7 @@ wasm::WasmCode* CompileWasmJSFastCallWrapper(wasm::NativeModule* native_module,
|
|||||||
result.tagged_parameter_slots,
|
result.tagged_parameter_slots,
|
||||||
result.protected_instructions_data.as_vector(),
|
result.protected_instructions_data.as_vector(),
|
||||||
result.source_positions.as_vector(), wasm::WasmCode::kWasmToJsWrapper,
|
result.source_positions.as_vector(), wasm::WasmCode::kWasmToJsWrapper,
|
||||||
wasm::ExecutionTier::kNone, wasm::kNotForDebugging);
|
wasm::ExecutionTier::kNone, wasm::kNoDebugging);
|
||||||
return native_module->PublishCode(std::move(wasm_code));
|
return native_module->PublishCode(std::move(wasm_code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,9 +958,9 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* v8_isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if V8_ENABLE_WEBASSEMBLY
|
#if V8_ENABLE_WEBASSEMBLY
|
||||||
void EnterDebuggingForIsolate(Isolate* v8_isolate) {
|
void TierDownAllModulesPerIsolate(Isolate* v8_isolate) {
|
||||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||||
i::wasm::GetWasmEngine()->EnterDebuggingForIsolate(isolate);
|
i::wasm::GetWasmEngine()->TierDownAllModulesPerIsolate(isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaveDebuggingForIsolate(Isolate* v8_isolate) {
|
void LeaveDebuggingForIsolate(Isolate* v8_isolate) {
|
||||||
|
@ -315,7 +315,7 @@ V8_EXPORT_PRIVATE void SetDebugDelegate(Isolate* isolate,
|
|||||||
DebugDelegate* listener);
|
DebugDelegate* listener);
|
||||||
|
|
||||||
#if V8_ENABLE_WEBASSEMBLY
|
#if V8_ENABLE_WEBASSEMBLY
|
||||||
V8_EXPORT_PRIVATE void EnterDebuggingForIsolate(Isolate* isolate);
|
V8_EXPORT_PRIVATE void TierDownAllModulesPerIsolate(Isolate* isolate);
|
||||||
V8_EXPORT_PRIVATE void LeaveDebuggingForIsolate(Isolate* isolate);
|
V8_EXPORT_PRIVATE void LeaveDebuggingForIsolate(Isolate* isolate);
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
|
||||||
|
@ -395,7 +395,7 @@ GdbServer::DebugDelegate::DebugDelegate(Isolate* isolate, GdbServer* gdb_server)
|
|||||||
|
|
||||||
// Register the delegate
|
// Register the delegate
|
||||||
isolate_->debug()->SetDebugDelegate(this);
|
isolate_->debug()->SetDebugDelegate(this);
|
||||||
v8::debug::EnterDebuggingForIsolate((v8::Isolate*)isolate_);
|
v8::debug::TierDownAllModulesPerIsolate((v8::Isolate*)isolate_);
|
||||||
v8::debug::ChangeBreakOnException((v8::Isolate*)isolate_,
|
v8::debug::ChangeBreakOnException((v8::Isolate*)isolate_,
|
||||||
v8::debug::BreakOnUncaughtException);
|
v8::debug::BreakOnUncaughtException);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ void V8Debugger::enable() {
|
|||||||
v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
|
v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
|
||||||
m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
|
m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
|
||||||
#if V8_ENABLE_WEBASSEMBLY
|
#if V8_ENABLE_WEBASSEMBLY
|
||||||
v8::debug::EnterDebuggingForIsolate(m_isolate);
|
v8::debug::TierDownAllModulesPerIsolate(m_isolate);
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,30 +432,13 @@ RUNTIME_FUNCTION(Runtime_WasmTierUpFunction) {
|
|||||||
return ReadOnlyRoots(isolate).undefined_value();
|
return ReadOnlyRoots(isolate).undefined_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_WasmEnterDebugging) {
|
RUNTIME_FUNCTION(Runtime_WasmTierDown) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK_EQ(0, args.length());
|
DCHECK_EQ(0, args.length());
|
||||||
wasm::GetWasmEngine()->EnterDebuggingForIsolate(isolate);
|
wasm::GetWasmEngine()->TierDownAllModulesPerIsolate(isolate);
|
||||||
return ReadOnlyRoots(isolate).undefined_value();
|
return ReadOnlyRoots(isolate).undefined_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_IsWasmDebugFunction) {
|
|
||||||
HandleScope scope(isolate);
|
|
||||||
DCHECK_EQ(1, args.length());
|
|
||||||
Handle<JSFunction> function = args.at<JSFunction>(0);
|
|
||||||
CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
|
|
||||||
Handle<WasmExportedFunction> exp_fun =
|
|
||||||
Handle<WasmExportedFunction>::cast(function);
|
|
||||||
wasm::NativeModule* native_module =
|
|
||||||
exp_fun->instance().module_object().native_module();
|
|
||||||
uint32_t func_index = exp_fun->function_index();
|
|
||||||
wasm::WasmCodeRefScope code_ref_scope;
|
|
||||||
wasm::WasmCode* code = native_module->GetCode(func_index);
|
|
||||||
return isolate->heap()->ToBoolean(
|
|
||||||
code && code->is_liftoff() &&
|
|
||||||
(code->for_debugging() != wasm::kNotForDebugging));
|
|
||||||
}
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
|
RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK_EQ(1, args.length());
|
DCHECK_EQ(1, args.length());
|
||||||
|
@ -654,7 +654,6 @@ namespace internal {
|
|||||||
F(IsAsmWasmCode, 1, 1) \
|
F(IsAsmWasmCode, 1, 1) \
|
||||||
F(IsLiftoffFunction, 1, 1) \
|
F(IsLiftoffFunction, 1, 1) \
|
||||||
F(IsTurboFanFunction, 1, 1) \
|
F(IsTurboFanFunction, 1, 1) \
|
||||||
F(IsWasmDebugFunction, 1, 1) \
|
|
||||||
F(IsThreadInWasm, 0, 1) \
|
F(IsThreadInWasm, 0, 1) \
|
||||||
F(IsWasmCode, 1, 1) \
|
F(IsWasmCode, 1, 1) \
|
||||||
F(IsWasmTrapHandlerEnabled, 0, 1) \
|
F(IsWasmTrapHandlerEnabled, 0, 1) \
|
||||||
@ -663,7 +662,7 @@ namespace internal {
|
|||||||
F(SetWasmInstantiateControls, 0, 1) \
|
F(SetWasmInstantiateControls, 0, 1) \
|
||||||
F(WasmGetNumberOfInstances, 1, 1) \
|
F(WasmGetNumberOfInstances, 1, 1) \
|
||||||
F(WasmNumCodeSpaces, 1, 1) \
|
F(WasmNumCodeSpaces, 1, 1) \
|
||||||
F(WasmEnterDebugging, 0, 1) \
|
F(WasmTierDown, 0, 1) \
|
||||||
F(WasmTierUpFunction, 2, 1) \
|
F(WasmTierUpFunction, 2, 1) \
|
||||||
F(WasmTraceEnter, 0, 1) \
|
F(WasmTraceEnter, 0, 1) \
|
||||||
F(WasmTraceExit, 1, 1) \
|
F(WasmTraceExit, 1, 1) \
|
||||||
|
@ -775,7 +775,7 @@ class LiftoffCompiler {
|
|||||||
// overflows in the budget calculation.
|
// overflows in the budget calculation.
|
||||||
DCHECK_LE(1, budget_used);
|
DCHECK_LE(1, budget_used);
|
||||||
|
|
||||||
if (for_debugging_ != kNotForDebugging) return;
|
if (for_debugging_ != kNoDebugging) return;
|
||||||
CODE_COMMENT("tierup check");
|
CODE_COMMENT("tierup check");
|
||||||
// We never want to blow the entire budget at once.
|
// We never want to blow the entire budget at once.
|
||||||
const int kMax = v8_flags.wasm_tiering_budget / 4;
|
const int kMax = v8_flags.wasm_tiering_budget / 4;
|
||||||
@ -857,7 +857,7 @@ class LiftoffCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool dynamic_tiering() {
|
bool dynamic_tiering() {
|
||||||
return env_->dynamic_tiering && for_debugging_ == kNotForDebugging &&
|
return env_->dynamic_tiering && for_debugging_ == kNoDebugging &&
|
||||||
(v8_flags.wasm_tier_up_filter == -1 ||
|
(v8_flags.wasm_tier_up_filter == -1 ||
|
||||||
v8_flags.wasm_tier_up_filter == func_index_);
|
v8_flags.wasm_tier_up_filter == func_index_);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ enum LiftoffBailoutReason : int8_t {
|
|||||||
|
|
||||||
struct LiftoffOptions {
|
struct LiftoffOptions {
|
||||||
int func_index = -1;
|
int func_index = -1;
|
||||||
ForDebugging for_debugging = kNotForDebugging;
|
ForDebugging for_debugging = kNoDebugging;
|
||||||
Counters* counters = nullptr;
|
Counters* counters = nullptr;
|
||||||
AssemblerBufferCache* assembler_buffer_cache = nullptr;
|
AssemblerBufferCache* assembler_buffer_cache = nullptr;
|
||||||
WasmFeatures* detected_features = nullptr;
|
WasmFeatures* detected_features = nullptr;
|
||||||
|
@ -124,6 +124,7 @@ enum class CompilationEvent : uint8_t {
|
|||||||
kFinishedExportWrappers,
|
kFinishedExportWrappers,
|
||||||
kFinishedCompilationChunk,
|
kFinishedCompilationChunk,
|
||||||
kFailedCompilation,
|
kFailedCompilation,
|
||||||
|
kFinishedRecompilation
|
||||||
};
|
};
|
||||||
|
|
||||||
class V8_EXPORT_PRIVATE CompilationEventCallback {
|
class V8_EXPORT_PRIVATE CompilationEventCallback {
|
||||||
@ -174,6 +175,7 @@ class V8_EXPORT_PRIVATE CompilationState {
|
|||||||
|
|
||||||
bool failed() const;
|
bool failed() const;
|
||||||
bool baseline_compilation_finished() const;
|
bool baseline_compilation_finished() const;
|
||||||
|
bool recompilation_finished() const;
|
||||||
|
|
||||||
void set_compilation_id(int compilation_id);
|
void set_compilation_id(int compilation_id);
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
|
|||||||
|
|
||||||
DCHECK_LE(native_module->num_imported_functions(), function->func_index);
|
DCHECK_LE(native_module->num_imported_functions(), function->func_index);
|
||||||
DCHECK_LT(function->func_index, native_module->num_functions());
|
DCHECK_LT(function->func_index, native_module->num_functions());
|
||||||
WasmCompilationUnit unit(function->func_index, tier, kNotForDebugging);
|
WasmCompilationUnit unit(function->func_index, tier, kNoDebugging);
|
||||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||||
WasmCompilationResult result = unit.ExecuteCompilation(
|
WasmCompilationResult result = unit.ExecuteCompilation(
|
||||||
&env, native_module->compilation_state()->GetWireBytesStorage().get(),
|
&env, native_module->compilation_state()->GetWireBytesStorage().get(),
|
||||||
|
@ -56,16 +56,13 @@ struct WasmCompilationResult {
|
|||||||
ExecutionTier requested_tier;
|
ExecutionTier requested_tier;
|
||||||
ExecutionTier result_tier;
|
ExecutionTier result_tier;
|
||||||
Kind kind = kFunction;
|
Kind kind = kFunction;
|
||||||
ForDebugging for_debugging = kNotForDebugging;
|
ForDebugging for_debugging = kNoDebugging;
|
||||||
};
|
};
|
||||||
|
|
||||||
class V8_EXPORT_PRIVATE WasmCompilationUnit final {
|
class V8_EXPORT_PRIVATE WasmCompilationUnit final {
|
||||||
public:
|
public:
|
||||||
WasmCompilationUnit(int index, ExecutionTier tier, ForDebugging for_debugging)
|
WasmCompilationUnit(int index, ExecutionTier tier, ForDebugging for_debugging)
|
||||||
: func_index_(index), tier_(tier), for_debugging_(for_debugging) {
|
: func_index_(index), tier_(tier), for_debugging_(for_debugging) {}
|
||||||
DCHECK_IMPLIES(for_debugging != ForDebugging::kNotForDebugging,
|
|
||||||
tier_ == ExecutionTier::kLiftoff);
|
|
||||||
}
|
|
||||||
|
|
||||||
WasmCompilationResult ExecuteCompilation(CompilationEnv*,
|
WasmCompilationResult ExecuteCompilation(CompilationEnv*,
|
||||||
const WireBytesStorage*, Counters*,
|
const WireBytesStorage*, Counters*,
|
||||||
|
@ -575,6 +575,8 @@ class CompilationStateImpl {
|
|||||||
int num_export_wrappers,
|
int num_export_wrappers,
|
||||||
ProfileInformation* pgo_info);
|
ProfileInformation* pgo_info);
|
||||||
|
|
||||||
|
// Initialize the compilation progress after deserialization. This is needed
|
||||||
|
// for recompilation (e.g. for tier down) to work later.
|
||||||
void InitializeCompilationProgressAfterDeserialization(
|
void InitializeCompilationProgressAfterDeserialization(
|
||||||
base::Vector<const int> lazy_functions,
|
base::Vector<const int> lazy_functions,
|
||||||
base::Vector<const int> eager_functions);
|
base::Vector<const int> eager_functions);
|
||||||
@ -589,6 +591,14 @@ class CompilationStateImpl {
|
|||||||
// equivalent to {InitializeCompilationUnits}.
|
// equivalent to {InitializeCompilationUnits}.
|
||||||
void AddCompilationUnit(CompilationUnitBuilder* builder, int func_index);
|
void AddCompilationUnit(CompilationUnitBuilder* builder, int func_index);
|
||||||
|
|
||||||
|
// Initialize recompilation of the whole module: Setup compilation progress
|
||||||
|
// for recompilation and add the respective compilation units. The callback is
|
||||||
|
// called immediately if no recompilation is needed, or called later
|
||||||
|
// otherwise.
|
||||||
|
void InitializeRecompilation(TieringState new_tiering_state,
|
||||||
|
std::unique_ptr<CompilationEventCallback>
|
||||||
|
recompilation_finished_callback);
|
||||||
|
|
||||||
// Add the callback to be called on compilation events. Needs to be
|
// Add the callback to be called on compilation events. Needs to be
|
||||||
// set before {CommitCompilationUnits} is run to ensure that it receives all
|
// set before {CommitCompilationUnits} is run to ensure that it receives all
|
||||||
// events. The callback object must support being deleted from any thread.
|
// events. The callback object must support being deleted from any thread.
|
||||||
@ -641,6 +651,11 @@ class CompilationStateImpl {
|
|||||||
outstanding_export_wrappers_ == 0;
|
outstanding_export_wrappers_ == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool recompilation_finished() const {
|
||||||
|
base::MutexGuard guard(&callbacks_mutex_);
|
||||||
|
return outstanding_recompilation_functions_ == 0;
|
||||||
|
}
|
||||||
|
|
||||||
DynamicTiering dynamic_tiering() const { return dynamic_tiering_; }
|
DynamicTiering dynamic_tiering() const { return dynamic_tiering_; }
|
||||||
|
|
||||||
Counters* counters() const { return async_counters_.get(); }
|
Counters* counters() const { return async_counters_.get(); }
|
||||||
@ -755,6 +770,9 @@ class CompilationStateImpl {
|
|||||||
size_t bytes_since_last_chunk_ = 0;
|
size_t bytes_since_last_chunk_ = 0;
|
||||||
std::vector<uint8_t> compilation_progress_;
|
std::vector<uint8_t> compilation_progress_;
|
||||||
|
|
||||||
|
int outstanding_recompilation_functions_ = 0;
|
||||||
|
TieringState tiering_state_ = kTieredUp;
|
||||||
|
|
||||||
// End of fields protected by {callbacks_mutex_}.
|
// End of fields protected by {callbacks_mutex_}.
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -767,6 +785,7 @@ class CompilationStateImpl {
|
|||||||
using RequiredBaselineTierField = base::BitField8<ExecutionTier, 0, 2>;
|
using RequiredBaselineTierField = base::BitField8<ExecutionTier, 0, 2>;
|
||||||
using RequiredTopTierField = base::BitField8<ExecutionTier, 2, 2>;
|
using RequiredTopTierField = base::BitField8<ExecutionTier, 2, 2>;
|
||||||
using ReachedTierField = base::BitField8<ExecutionTier, 4, 2>;
|
using ReachedTierField = base::BitField8<ExecutionTier, 4, 2>;
|
||||||
|
using MissingRecompilationField = base::BitField8<bool, 6, 1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
CompilationStateImpl* Impl(CompilationState* compilation_state) {
|
CompilationStateImpl* Impl(CompilationState* compilation_state) {
|
||||||
@ -849,6 +868,10 @@ bool CompilationState::baseline_compilation_finished() const {
|
|||||||
return Impl(this)->baseline_compilation_finished();
|
return Impl(this)->baseline_compilation_finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CompilationState::recompilation_finished() const {
|
||||||
|
return Impl(this)->recompilation_finished();
|
||||||
|
}
|
||||||
|
|
||||||
void CompilationState::set_compilation_id(int compilation_id) {
|
void CompilationState::set_compilation_id(int compilation_id) {
|
||||||
Impl(this)->set_compilation_id(compilation_id);
|
Impl(this)->set_compilation_id(compilation_id);
|
||||||
}
|
}
|
||||||
@ -922,19 +945,15 @@ struct ExecutionTierPair {
|
|||||||
ExecutionTier top_tier;
|
ExecutionTier top_tier;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pass the debug state as a separate parameter to avoid data races: the debug
|
|
||||||
// state may change between its use here and its use at the call site. To have
|
|
||||||
// a consistent view on the debug state, the caller reads the debug state once
|
|
||||||
// and then passes it to this function.
|
|
||||||
ExecutionTierPair GetDefaultTiersPerModule(NativeModule* native_module,
|
ExecutionTierPair GetDefaultTiersPerModule(NativeModule* native_module,
|
||||||
DynamicTiering dynamic_tiering,
|
DynamicTiering dynamic_tiering,
|
||||||
DebugState is_in_debug_state,
|
|
||||||
bool lazy_module) {
|
bool lazy_module) {
|
||||||
const WasmModule* module = native_module->module();
|
const WasmModule* module = native_module->module();
|
||||||
if (is_asmjs_module(module)) {
|
if (is_asmjs_module(module)) {
|
||||||
return {ExecutionTier::kTurbofan, ExecutionTier::kTurbofan};
|
return {ExecutionTier::kTurbofan, ExecutionTier::kTurbofan};
|
||||||
}
|
}
|
||||||
if (is_in_debug_state) {
|
// TODO(13224): Use lazy compilation for debug code.
|
||||||
|
if (native_module->IsTieredDown()) {
|
||||||
return {ExecutionTier::kLiftoff, ExecutionTier::kLiftoff};
|
return {ExecutionTier::kLiftoff, ExecutionTier::kLiftoff};
|
||||||
}
|
}
|
||||||
if (lazy_module) {
|
if (lazy_module) {
|
||||||
@ -949,17 +968,14 @@ ExecutionTierPair GetDefaultTiersPerModule(NativeModule* native_module,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExecutionTierPair GetLazyCompilationTiers(NativeModule* native_module,
|
ExecutionTierPair GetLazyCompilationTiers(NativeModule* native_module,
|
||||||
uint32_t func_index,
|
uint32_t func_index) {
|
||||||
DebugState is_in_debug_state) {
|
|
||||||
DynamicTiering dynamic_tiering =
|
DynamicTiering dynamic_tiering =
|
||||||
Impl(native_module->compilation_state())->dynamic_tiering();
|
Impl(native_module->compilation_state())->dynamic_tiering();
|
||||||
// For lazy compilation, get the tiers we would use if lazy compilation is
|
// For lazy compilation, get the tiers we would use if lazy compilation is
|
||||||
// disabled.
|
// disabled.
|
||||||
constexpr bool kNotLazy = false;
|
constexpr bool kNotLazy = false;
|
||||||
ExecutionTierPair tiers = GetDefaultTiersPerModule(
|
ExecutionTierPair tiers =
|
||||||
native_module, dynamic_tiering, is_in_debug_state, kNotLazy);
|
GetDefaultTiersPerModule(native_module, dynamic_tiering, kNotLazy);
|
||||||
// If we are in debug mode, we ignore compilation hints.
|
|
||||||
if (is_in_debug_state) return tiers;
|
|
||||||
|
|
||||||
// Check if compilation hints override default tiering behaviour.
|
// Check if compilation hints override default tiering behaviour.
|
||||||
if (native_module->enabled_features().has_compilation_hints()) {
|
if (native_module->enabled_features().has_compilation_hints()) {
|
||||||
@ -996,7 +1012,7 @@ class CompilationUnitBuilder {
|
|||||||
void AddImportUnit(uint32_t func_index) {
|
void AddImportUnit(uint32_t func_index) {
|
||||||
DCHECK_GT(native_module_->module()->num_imported_functions, func_index);
|
DCHECK_GT(native_module_->module()->num_imported_functions, func_index);
|
||||||
baseline_units_.emplace_back(func_index, ExecutionTier::kNone,
|
baseline_units_.emplace_back(func_index, ExecutionTier::kNone,
|
||||||
kNotForDebugging);
|
kNoDebugging);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddJSToWasmWrapperUnit(
|
void AddJSToWasmWrapperUnit(
|
||||||
@ -1005,11 +1021,11 @@ class CompilationUnitBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AddBaselineUnit(int func_index, ExecutionTier tier) {
|
void AddBaselineUnit(int func_index, ExecutionTier tier) {
|
||||||
baseline_units_.emplace_back(func_index, tier, kNotForDebugging);
|
baseline_units_.emplace_back(func_index, tier, kNoDebugging);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTopTierUnit(int func_index, ExecutionTier tier) {
|
void AddTopTierUnit(int func_index, ExecutionTier tier) {
|
||||||
tiering_units_.emplace_back(func_index, tier, kNotForDebugging);
|
tiering_units_.emplace_back(func_index, tier, kNoDebugging);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddDebugUnit(int func_index) {
|
void AddDebugUnit(int func_index) {
|
||||||
@ -1017,6 +1033,13 @@ class CompilationUnitBuilder {
|
|||||||
kForDebugging);
|
kForDebugging);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddRecompilationUnit(int func_index, ExecutionTier tier) {
|
||||||
|
// For recompilation, just treat all units like baseline units.
|
||||||
|
baseline_units_.emplace_back(
|
||||||
|
func_index, tier,
|
||||||
|
tier == ExecutionTier::kLiftoff ? kForDebugging : kNoDebugging);
|
||||||
|
}
|
||||||
|
|
||||||
bool Commit() {
|
bool Commit() {
|
||||||
if (baseline_units_.empty() && tiering_units_.empty() &&
|
if (baseline_units_.empty() && tiering_units_.empty() &&
|
||||||
js_to_wasm_wrapper_units_.empty()) {
|
js_to_wasm_wrapper_units_.empty()) {
|
||||||
@ -1116,15 +1139,12 @@ bool CompileLazy(Isolate* isolate, WasmInstanceObject instance,
|
|||||||
|
|
||||||
CompilationStateImpl* compilation_state =
|
CompilationStateImpl* compilation_state =
|
||||||
Impl(native_module->compilation_state());
|
Impl(native_module->compilation_state());
|
||||||
DebugState is_in_debug_state = native_module->IsInDebugState();
|
ExecutionTierPair tiers = GetLazyCompilationTiers(native_module, func_index);
|
||||||
ExecutionTierPair tiers =
|
|
||||||
GetLazyCompilationTiers(native_module, func_index, is_in_debug_state);
|
|
||||||
|
|
||||||
DCHECK_LE(native_module->num_imported_functions(), func_index);
|
DCHECK_LE(native_module->num_imported_functions(), func_index);
|
||||||
DCHECK_LT(func_index, native_module->num_functions());
|
DCHECK_LT(func_index, native_module->num_functions());
|
||||||
WasmCompilationUnit baseline_unit{
|
WasmCompilationUnit baseline_unit{func_index, tiers.baseline_tier,
|
||||||
func_index, tiers.baseline_tier,
|
kNoDebugging};
|
||||||
is_in_debug_state ? kForDebugging : kNotForDebugging};
|
|
||||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||||
// TODO(wasm): Use an assembler buffer cache for lazy compilation.
|
// TODO(wasm): Use an assembler buffer cache for lazy compilation.
|
||||||
AssemblerBufferCache* assembler_buffer_cache = nullptr;
|
AssemblerBufferCache* assembler_buffer_cache = nullptr;
|
||||||
@ -1171,8 +1191,7 @@ bool CompileLazy(Isolate* isolate, WasmInstanceObject instance,
|
|||||||
if (GetCompileStrategy(module, native_module->enabled_features(), func_index,
|
if (GetCompileStrategy(module, native_module->enabled_features(), func_index,
|
||||||
lazy_module) == CompileStrategy::kLazy &&
|
lazy_module) == CompileStrategy::kLazy &&
|
||||||
tiers.baseline_tier < tiers.top_tier) {
|
tiers.baseline_tier < tiers.top_tier) {
|
||||||
WasmCompilationUnit tiering_unit{func_index, tiers.top_tier,
|
WasmCompilationUnit tiering_unit{func_index, tiers.top_tier, kNoDebugging};
|
||||||
kNotForDebugging};
|
|
||||||
compilation_state->CommitTopTierCompilationUnit(tiering_unit);
|
compilation_state->CommitTopTierCompilationUnit(tiering_unit);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1383,7 +1402,7 @@ void TriggerTierUp(WasmInstanceObject instance, int func_index) {
|
|||||||
CompilationStateImpl* compilation_state =
|
CompilationStateImpl* compilation_state =
|
||||||
Impl(native_module->compilation_state());
|
Impl(native_module->compilation_state());
|
||||||
WasmCompilationUnit tiering_unit{func_index, ExecutionTier::kTurbofan,
|
WasmCompilationUnit tiering_unit{func_index, ExecutionTier::kTurbofan,
|
||||||
kNotForDebugging};
|
kNoDebugging};
|
||||||
|
|
||||||
const WasmModule* module = native_module->module();
|
const WasmModule* module = native_module->module();
|
||||||
int priority;
|
int priority;
|
||||||
@ -2000,6 +2019,44 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
|
|||||||
return native_module;
|
return native_module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecompileNativeModule(NativeModule* native_module,
|
||||||
|
TieringState tiering_state) {
|
||||||
|
// Install a callback to notify us once background recompilation finished.
|
||||||
|
auto recompilation_finished_semaphore = std::make_shared<base::Semaphore>(0);
|
||||||
|
auto* compilation_state = Impl(native_module->compilation_state());
|
||||||
|
|
||||||
|
class RecompilationFinishedCallback : public CompilationEventCallback {
|
||||||
|
public:
|
||||||
|
explicit RecompilationFinishedCallback(
|
||||||
|
std::shared_ptr<base::Semaphore> recompilation_finished_semaphore)
|
||||||
|
: recompilation_finished_semaphore_(
|
||||||
|
std::move(recompilation_finished_semaphore)) {}
|
||||||
|
|
||||||
|
void call(CompilationEvent event) override {
|
||||||
|
DCHECK_NE(CompilationEvent::kFailedCompilation, event);
|
||||||
|
if (event == CompilationEvent::kFinishedRecompilation) {
|
||||||
|
recompilation_finished_semaphore_->Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<base::Semaphore> recompilation_finished_semaphore_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The callback captures a shared ptr to the semaphore.
|
||||||
|
// Initialize the compilation units and kick off background compile tasks.
|
||||||
|
compilation_state->InitializeRecompilation(
|
||||||
|
tiering_state, std::make_unique<RecompilationFinishedCallback>(
|
||||||
|
recompilation_finished_semaphore));
|
||||||
|
|
||||||
|
constexpr JobDelegate* kNoDelegate = nullptr;
|
||||||
|
ExecuteCompilationUnits(compilation_state->native_module_weak(),
|
||||||
|
compilation_state->counters(), kNoDelegate,
|
||||||
|
kBaselineOnly);
|
||||||
|
recompilation_finished_semaphore->Wait();
|
||||||
|
DCHECK(!compilation_state->failed());
|
||||||
|
}
|
||||||
|
|
||||||
AsyncCompileJob::AsyncCompileJob(
|
AsyncCompileJob::AsyncCompileJob(
|
||||||
Isolate* isolate, WasmFeatures enabled_features,
|
Isolate* isolate, WasmFeatures enabled_features,
|
||||||
base::OwnedVector<const uint8_t> bytes, Handle<Context> context,
|
base::OwnedVector<const uint8_t> bytes, Handle<Context> context,
|
||||||
@ -2308,10 +2365,11 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
|
|||||||
// We can only update the feature counts once the entire compile is done.
|
// We can only update the feature counts once the entire compile is done.
|
||||||
compilation_state->PublishDetectedFeatures(isolate_);
|
compilation_state->PublishDetectedFeatures(isolate_);
|
||||||
|
|
||||||
// We might need debug code for the module, if the debugger was enabled while
|
// We might need to recompile the module for debugging, if the debugger was
|
||||||
// streaming compilation was running. Since handling this while compiling via
|
// enabled while streaming compilation was running. Since handling this while
|
||||||
// streaming is tricky, we just tier down now, before publishing the module.
|
// compiling via streaming is tricky, we just tier down now, before publishing
|
||||||
if (native_module_->IsInDebugState()) native_module_->RemoveAllCompiledCode();
|
// the module.
|
||||||
|
if (native_module_->IsTieredDown()) native_module_->RecompileForTiering();
|
||||||
|
|
||||||
// Finally, log all generated code (it does not matter if this happens
|
// Finally, log all generated code (it does not matter if this happens
|
||||||
// repeatedly in case the script is shared).
|
// repeatedly in case the script is shared).
|
||||||
@ -2379,6 +2437,10 @@ class AsyncCompileJob::CompilationStateCallback
|
|||||||
job_->DoSync<Fail>();
|
job_->DoSync<Fail>();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CompilationEvent::kFinishedRecompilation:
|
||||||
|
// This event can happen out of order, hence don't remember this in
|
||||||
|
// {last_event_}.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
last_event_ = event;
|
last_event_ = event;
|
||||||
@ -3138,8 +3200,7 @@ void CompilationStateImpl::InitializeCompilationProgress(
|
|||||||
|
|
||||||
// Compute the default compilation progress for all functions, and set it.
|
// Compute the default compilation progress for all functions, and set it.
|
||||||
const ExecutionTierPair default_tiers = GetDefaultTiersPerModule(
|
const ExecutionTierPair default_tiers = GetDefaultTiersPerModule(
|
||||||
native_module_, dynamic_tiering_, native_module_->IsInDebugState(),
|
native_module_, dynamic_tiering_, IsLazyModule(module));
|
||||||
IsLazyModule(module));
|
|
||||||
const uint8_t default_progress =
|
const uint8_t default_progress =
|
||||||
RequiredBaselineTierField::encode(default_tiers.baseline_tier) |
|
RequiredBaselineTierField::encode(default_tiers.baseline_tier) |
|
||||||
RequiredTopTierField::encode(default_tiers.top_tier) |
|
RequiredTopTierField::encode(default_tiers.top_tier) |
|
||||||
@ -3219,7 +3280,7 @@ uint8_t CompilationStateImpl::AddCompilationUnitInternal(
|
|||||||
void CompilationStateImpl::InitializeCompilationUnits(
|
void CompilationStateImpl::InitializeCompilationUnits(
|
||||||
std::unique_ptr<CompilationUnitBuilder> builder) {
|
std::unique_ptr<CompilationUnitBuilder> builder) {
|
||||||
int offset = native_module_->module()->num_imported_functions;
|
int offset = native_module_->module()->num_imported_functions;
|
||||||
if (native_module_->IsInDebugState()) {
|
if (native_module_->IsTieredDown()) {
|
||||||
for (size_t i = 0; i < compilation_progress_.size(); ++i) {
|
for (size_t i = 0; i < compilation_progress_.size(); ++i) {
|
||||||
int func_index = offset + static_cast<int>(i);
|
int func_index = offset + static_cast<int>(i);
|
||||||
builder->AddDebugUnit(func_index);
|
builder->AddDebugUnit(func_index);
|
||||||
@ -3239,6 +3300,10 @@ void CompilationStateImpl::InitializeCompilationUnits(
|
|||||||
|
|
||||||
void CompilationStateImpl::AddCompilationUnit(CompilationUnitBuilder* builder,
|
void CompilationStateImpl::AddCompilationUnit(CompilationUnitBuilder* builder,
|
||||||
int func_index) {
|
int func_index) {
|
||||||
|
if (native_module_->IsTieredDown()) {
|
||||||
|
builder->AddDebugUnit(func_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int offset = native_module_->module()->num_imported_functions;
|
int offset = native_module_->module()->num_imported_functions;
|
||||||
int progress_index = func_index - offset;
|
int progress_index = func_index - offset;
|
||||||
uint8_t function_progress;
|
uint8_t function_progress;
|
||||||
@ -3307,8 +3372,7 @@ void CompilationStateImpl::InitializeCompilationProgressAfterDeserialization(
|
|||||||
// Update compilation state for eagerly compiled functions.
|
// Update compilation state for eagerly compiled functions.
|
||||||
constexpr bool kNotLazy = false;
|
constexpr bool kNotLazy = false;
|
||||||
ExecutionTierPair default_tiers =
|
ExecutionTierPair default_tiers =
|
||||||
GetDefaultTiersPerModule(native_module_, dynamic_tiering_,
|
GetDefaultTiersPerModule(native_module_, dynamic_tiering_, kNotLazy);
|
||||||
native_module_->IsInDebugState(), kNotLazy);
|
|
||||||
uint8_t progress_for_eager_functions =
|
uint8_t progress_for_eager_functions =
|
||||||
RequiredBaselineTierField::encode(default_tiers.baseline_tier) |
|
RequiredBaselineTierField::encode(default_tiers.baseline_tier) |
|
||||||
RequiredTopTierField::encode(default_tiers.top_tier) |
|
RequiredTopTierField::encode(default_tiers.top_tier) |
|
||||||
@ -3337,6 +3401,87 @@ void CompilationStateImpl::InitializeCompilationProgressAfterDeserialization(
|
|||||||
WaitForCompilationEvent(CompilationEvent::kFinishedBaselineCompilation);
|
WaitForCompilationEvent(CompilationEvent::kFinishedBaselineCompilation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilationStateImpl::InitializeRecompilation(
|
||||||
|
TieringState new_tiering_state,
|
||||||
|
std::unique_ptr<CompilationEventCallback> recompilation_finished_callback) {
|
||||||
|
DCHECK(!failed());
|
||||||
|
|
||||||
|
// Hold the mutex as long as possible, to synchronize between multiple
|
||||||
|
// recompilations that are triggered at the same time (e.g. when the profiler
|
||||||
|
// is disabled).
|
||||||
|
base::Optional<base::MutexGuard> guard(&callbacks_mutex_);
|
||||||
|
|
||||||
|
// As long as there are outstanding recompilation functions, take part in
|
||||||
|
// compilation. This is to avoid recompiling for the same tier or for
|
||||||
|
// different tiers concurrently. Note that the compilation unit queues can run
|
||||||
|
// empty before {outstanding_recompilation_functions_} drops to zero. In this
|
||||||
|
// case, we do not wait for the last running compilation threads to finish
|
||||||
|
// their units, but just start our own recompilation already.
|
||||||
|
while (outstanding_recompilation_functions_ > 0 &&
|
||||||
|
compilation_unit_queues_.GetTotalSize() > 0) {
|
||||||
|
guard.reset();
|
||||||
|
constexpr JobDelegate* kNoDelegate = nullptr;
|
||||||
|
ExecuteCompilationUnits(native_module_weak_, async_counters_.get(),
|
||||||
|
kNoDelegate, kBaselineOrTopTier);
|
||||||
|
guard.emplace(&callbacks_mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Information about compilation progress is shared between this class and the
|
||||||
|
// NativeModule. Before updating information here, consult the NativeModule to
|
||||||
|
// find all functions that need recompilation.
|
||||||
|
// Since the current tiering state is updated on the NativeModule before
|
||||||
|
// triggering recompilation, it's OK if the information is slightly outdated.
|
||||||
|
// If we compile functions twice, the NativeModule will ignore all redundant
|
||||||
|
// code (or code compiled for the wrong tier).
|
||||||
|
std::vector<int> recompile_function_indexes =
|
||||||
|
native_module_->FindFunctionsToRecompile(new_tiering_state);
|
||||||
|
|
||||||
|
callbacks_.emplace_back(std::move(recompilation_finished_callback));
|
||||||
|
tiering_state_ = new_tiering_state;
|
||||||
|
|
||||||
|
// If compilation progress is not initialized yet, then compilation didn't
|
||||||
|
// start yet, and new code will be kept tiered-down from the start. For
|
||||||
|
// streaming compilation, there is a special path to tier down later, when
|
||||||
|
// the module is complete. In any case, we don't need to recompile here.
|
||||||
|
base::Optional<CompilationUnitBuilder> builder;
|
||||||
|
if (compilation_progress_.size() > 0) {
|
||||||
|
builder.emplace(native_module_);
|
||||||
|
const WasmModule* module = native_module_->module();
|
||||||
|
DCHECK_EQ(module->num_declared_functions, compilation_progress_.size());
|
||||||
|
DCHECK_GE(module->num_declared_functions,
|
||||||
|
recompile_function_indexes.size());
|
||||||
|
outstanding_recompilation_functions_ =
|
||||||
|
static_cast<int>(recompile_function_indexes.size());
|
||||||
|
// Restart recompilation if another recompilation is already happening.
|
||||||
|
for (auto& progress : compilation_progress_) {
|
||||||
|
progress = MissingRecompilationField::update(progress, false);
|
||||||
|
}
|
||||||
|
auto new_tier = new_tiering_state == kTieredDown ? ExecutionTier::kLiftoff
|
||||||
|
: ExecutionTier::kTurbofan;
|
||||||
|
int imported = module->num_imported_functions;
|
||||||
|
// Generate necessary compilation units on the fly.
|
||||||
|
for (int function_index : recompile_function_indexes) {
|
||||||
|
DCHECK_LE(imported, function_index);
|
||||||
|
int slot_index = function_index - imported;
|
||||||
|
auto& progress = compilation_progress_[slot_index];
|
||||||
|
progress = MissingRecompilationField::update(progress, true);
|
||||||
|
builder->AddRecompilationUnit(function_index, new_tier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger callback if module needs no recompilation.
|
||||||
|
if (outstanding_recompilation_functions_ == 0) {
|
||||||
|
TriggerCallbacks(base::EnumSet<CompilationEvent>(
|
||||||
|
{CompilationEvent::kFinishedRecompilation}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.has_value()) {
|
||||||
|
// Avoid holding lock while scheduling a compile job.
|
||||||
|
guard.reset();
|
||||||
|
builder->Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CompilationStateImpl::AddCallback(
|
void CompilationStateImpl::AddCallback(
|
||||||
std::unique_ptr<CompilationEventCallback> callback) {
|
std::unique_ptr<CompilationEventCallback> callback) {
|
||||||
base::MutexGuard callbacks_guard(&callbacks_mutex_);
|
base::MutexGuard callbacks_guard(&callbacks_mutex_);
|
||||||
@ -3498,6 +3643,25 @@ void CompilationStateImpl::OnFinishedUnits(
|
|||||||
bytes_since_last_chunk_ += code->instructions().size();
|
bytes_since_last_chunk_ += code->instructions().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (V8_UNLIKELY(MissingRecompilationField::decode(function_progress))) {
|
||||||
|
DCHECK_LT(0, outstanding_recompilation_functions_);
|
||||||
|
// If tiering up, accept any TurboFan code. For tiering down, look at
|
||||||
|
// the {for_debugging} flag. The tier can be Liftoff or TurboFan and is
|
||||||
|
// irrelevant here. In particular, we want to ignore any outstanding
|
||||||
|
// non-debugging units.
|
||||||
|
bool matches = tiering_state_ == kTieredDown
|
||||||
|
? code->for_debugging()
|
||||||
|
: code->tier() == ExecutionTier::kTurbofan;
|
||||||
|
if (matches) {
|
||||||
|
outstanding_recompilation_functions_--;
|
||||||
|
compilation_progress_[slot_index] = MissingRecompilationField::update(
|
||||||
|
compilation_progress_[slot_index], false);
|
||||||
|
if (outstanding_recompilation_functions_ == 0) {
|
||||||
|
triggered_events.Add(CompilationEvent::kFinishedRecompilation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update function's compilation progress.
|
// Update function's compilation progress.
|
||||||
if (code->tier() > reached_tier) {
|
if (code->tier() > reached_tier) {
|
||||||
compilation_progress_[slot_index] = ReachedTierField::update(
|
compilation_progress_[slot_index] = ReachedTierField::update(
|
||||||
@ -3547,9 +3711,11 @@ void CompilationStateImpl::TriggerCallbacks(
|
|||||||
|
|
||||||
// Don't trigger past events again.
|
// Don't trigger past events again.
|
||||||
triggered_events -= finished_events_;
|
triggered_events -= finished_events_;
|
||||||
// There can be multiple compilation chunks, thus do not store this.
|
// Recompilation can happen multiple times, thus do not store this. There can
|
||||||
finished_events_ |=
|
// also be multiple compilation chunks.
|
||||||
triggered_events - CompilationEvent::kFinishedCompilationChunk;
|
finished_events_ |= triggered_events -
|
||||||
|
CompilationEvent::kFinishedRecompilation -
|
||||||
|
CompilationEvent::kFinishedCompilationChunk;
|
||||||
|
|
||||||
for (auto event :
|
for (auto event :
|
||||||
{std::make_pair(CompilationEvent::kFailedCompilation,
|
{std::make_pair(CompilationEvent::kFailedCompilation,
|
||||||
@ -3559,7 +3725,9 @@ void CompilationStateImpl::TriggerCallbacks(
|
|||||||
std::make_pair(CompilationEvent::kFinishedBaselineCompilation,
|
std::make_pair(CompilationEvent::kFinishedBaselineCompilation,
|
||||||
"wasm.BaselineFinished"),
|
"wasm.BaselineFinished"),
|
||||||
std::make_pair(CompilationEvent::kFinishedCompilationChunk,
|
std::make_pair(CompilationEvent::kFinishedCompilationChunk,
|
||||||
"wasm.CompilationChunkFinished")}) {
|
"wasm.CompilationChunkFinished"),
|
||||||
|
std::make_pair(CompilationEvent::kFinishedRecompilation,
|
||||||
|
"wasm.RecompilationFinished")}) {
|
||||||
if (!triggered_events.contains(event.first)) continue;
|
if (!triggered_events.contains(event.first)) continue;
|
||||||
DCHECK_NE(compilation_id_, kInvalidCompilationID);
|
DCHECK_NE(compilation_id_, kInvalidCompilationID);
|
||||||
TRACE_EVENT1("v8.wasm", event.second, "id", compilation_id_);
|
TRACE_EVENT1("v8.wasm", event.second, "id", compilation_id_);
|
||||||
@ -3568,7 +3736,8 @@ void CompilationStateImpl::TriggerCallbacks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0) {
|
if (outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0 &&
|
||||||
|
outstanding_recompilation_functions_ == 0) {
|
||||||
auto new_end = std::remove_if(
|
auto new_end = std::remove_if(
|
||||||
callbacks_.begin(), callbacks_.end(), [](const auto& callback) {
|
callbacks_.begin(), callbacks_.end(), [](const auto& callback) {
|
||||||
return callback->release_after_final_event();
|
return callback->release_after_final_event();
|
||||||
@ -3881,7 +4050,7 @@ WasmCode* CompileImportWrapper(
|
|||||||
result.tagged_parameter_slots,
|
result.tagged_parameter_slots,
|
||||||
result.protected_instructions_data.as_vector(),
|
result.protected_instructions_data.as_vector(),
|
||||||
result.source_positions.as_vector(), GetCodeKind(result),
|
result.source_positions.as_vector(), GetCodeKind(result),
|
||||||
ExecutionTier::kNone, kNotForDebugging);
|
ExecutionTier::kNone, kNoDebugging);
|
||||||
published_code = native_module->PublishCode(std::move(wasm_code));
|
published_code = native_module->PublishCode(std::move(wasm_code));
|
||||||
}
|
}
|
||||||
(*cache_scope)[key] = published_code;
|
(*cache_scope)[key] = published_code;
|
||||||
|
@ -61,6 +61,9 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
|
|||||||
int compilation_id, v8::metrics::Recorder::ContextId context_id,
|
int compilation_id, v8::metrics::Recorder::ContextId context_id,
|
||||||
ProfileInformation* pgo_info);
|
ProfileInformation* pgo_info);
|
||||||
|
|
||||||
|
void RecompileNativeModule(NativeModule* native_module,
|
||||||
|
TieringState new_tiering_state);
|
||||||
|
|
||||||
V8_EXPORT_PRIVATE
|
V8_EXPORT_PRIVATE
|
||||||
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module);
|
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module);
|
||||||
|
|
||||||
|
@ -1144,7 +1144,7 @@ WasmCode* NativeModule::AddCodeForTesting(Handle<Code> code) {
|
|||||||
source_pos.as_vector(), // source positions
|
source_pos.as_vector(), // source positions
|
||||||
WasmCode::kWasmFunction, // kind
|
WasmCode::kWasmFunction, // kind
|
||||||
ExecutionTier::kNone, // tier
|
ExecutionTier::kNone, // tier
|
||||||
kNotForDebugging}}; // for_debugging
|
kNoDebugging}}; // for_debugging
|
||||||
new_code->MaybePrint();
|
new_code->MaybePrint();
|
||||||
new_code->Validate();
|
new_code->Validate();
|
||||||
|
|
||||||
@ -1344,13 +1344,13 @@ WasmCode* NativeModule::PublishCodeLocked(
|
|||||||
// code table of jump table). Otherwise, install code if it was compiled
|
// code table of jump table). Otherwise, install code if it was compiled
|
||||||
// with a higher tier.
|
// with a higher tier.
|
||||||
static_assert(
|
static_assert(
|
||||||
kForDebugging > kNotForDebugging && kWithBreakpoints > kForDebugging,
|
kForDebugging > kNoDebugging && kWithBreakpoints > kForDebugging,
|
||||||
"for_debugging is ordered");
|
"for_debugging is ordered");
|
||||||
const bool update_code_table =
|
const bool update_code_table =
|
||||||
// Never install stepping code.
|
// Never install stepping code.
|
||||||
code->for_debugging() != kForStepping &&
|
code->for_debugging() != kForStepping &&
|
||||||
(!prior_code ||
|
(!prior_code ||
|
||||||
(debug_state_ == kDebugging
|
(tiering_state_ == kTieredDown
|
||||||
// Tiered down: Install breakpoints over normal debug code.
|
// Tiered down: Install breakpoints over normal debug code.
|
||||||
? prior_code->for_debugging() <= code->for_debugging()
|
? prior_code->for_debugging() <= code->for_debugging()
|
||||||
// Tiered up: Install if the tier is higher than before or we
|
// Tiered up: Install if the tier is higher than before or we
|
||||||
@ -1387,7 +1387,7 @@ void NativeModule::ReinstallDebugCode(WasmCode* code) {
|
|||||||
DCHECK_LT(code->index(), num_functions());
|
DCHECK_LT(code->index(), num_functions());
|
||||||
|
|
||||||
// If the module is tiered up by now, do not reinstall debug code.
|
// If the module is tiered up by now, do not reinstall debug code.
|
||||||
if (debug_state_ != kDebugging) return;
|
if (tiering_state_ != kTieredDown) return;
|
||||||
|
|
||||||
uint32_t slot_idx = declared_function_index(module(), code->index());
|
uint32_t slot_idx = declared_function_index(module(), code->index());
|
||||||
if (WasmCode* prior_code = code_table_[slot_idx]) {
|
if (WasmCode* prior_code = code_table_[slot_idx]) {
|
||||||
@ -1422,13 +1422,13 @@ std::unique_ptr<WasmCode> NativeModule::AddDeserializedCode(
|
|||||||
base::Vector<const byte> reloc_info,
|
base::Vector<const byte> reloc_info,
|
||||||
base::Vector<const byte> source_position_table, WasmCode::Kind kind,
|
base::Vector<const byte> source_position_table, WasmCode::Kind kind,
|
||||||
ExecutionTier tier) {
|
ExecutionTier tier) {
|
||||||
UpdateCodeSize(instructions.size(), tier, kNotForDebugging);
|
UpdateCodeSize(instructions.size(), tier, kNoDebugging);
|
||||||
|
|
||||||
return std::unique_ptr<WasmCode>{new WasmCode{
|
return std::unique_ptr<WasmCode>{new WasmCode{
|
||||||
this, index, instructions, stack_slots, tagged_parameter_slots,
|
this, index, instructions, stack_slots, tagged_parameter_slots,
|
||||||
safepoint_table_offset, handler_table_offset, constant_pool_offset,
|
safepoint_table_offset, handler_table_offset, constant_pool_offset,
|
||||||
code_comments_offset, unpadded_binary_size, protected_instructions_data,
|
code_comments_offset, unpadded_binary_size, protected_instructions_data,
|
||||||
reloc_info, source_position_table, kind, tier, kNotForDebugging}};
|
reloc_info, source_position_table, kind, tier, kNoDebugging}};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const {
|
std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const {
|
||||||
@ -1506,7 +1506,7 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegionLocked(
|
|||||||
base::Vector<uint8_t> code_space =
|
base::Vector<uint8_t> code_space =
|
||||||
code_allocator_.AllocateForCodeInRegion(this, jump_table_size, region);
|
code_allocator_.AllocateForCodeInRegion(this, jump_table_size, region);
|
||||||
DCHECK(!code_space.empty());
|
DCHECK(!code_space.empty());
|
||||||
UpdateCodeSize(jump_table_size, ExecutionTier::kNone, kNotForDebugging);
|
UpdateCodeSize(jump_table_size, ExecutionTier::kNone, kNoDebugging);
|
||||||
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{
|
std::unique_ptr<WasmCode> code{
|
||||||
new WasmCode{this, // native_module
|
new WasmCode{this, // native_module
|
||||||
@ -1524,13 +1524,13 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegionLocked(
|
|||||||
{}, // source_pos
|
{}, // source_pos
|
||||||
WasmCode::kJumpTable, // kind
|
WasmCode::kJumpTable, // kind
|
||||||
ExecutionTier::kNone, // tier
|
ExecutionTier::kNone, // tier
|
||||||
kNotForDebugging}}; // for_debugging
|
kNoDebugging}}; // for_debugging
|
||||||
return PublishCodeLocked(std::move(code));
|
return PublishCodeLocked(std::move(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeModule::UpdateCodeSize(size_t size, ExecutionTier tier,
|
void NativeModule::UpdateCodeSize(size_t size, ExecutionTier tier,
|
||||||
ForDebugging for_debugging) {
|
ForDebugging for_debugging) {
|
||||||
if (for_debugging != kNotForDebugging) return;
|
if (for_debugging != kNoDebugging) return;
|
||||||
// Count jump tables (ExecutionTier::kNone) for both Liftoff and TurboFan as
|
// Count jump tables (ExecutionTier::kNone) for both Liftoff and TurboFan as
|
||||||
// this is shared code.
|
// this is shared code.
|
||||||
if (tier != ExecutionTier::kTurbofan) liftoff_code_size_.fetch_add(size);
|
if (tier != ExecutionTier::kTurbofan) liftoff_code_size_.fetch_add(size);
|
||||||
@ -2410,12 +2410,17 @@ std::vector<std::unique_ptr<WasmCode>> NativeModule::AddCompiledCode(
|
|||||||
return generated_code;
|
return generated_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeModule::SetDebugState(DebugState new_debug_state) {
|
void NativeModule::SetTieringState(TieringState new_tiering_state) {
|
||||||
// Do not tier down asm.js (just never change the tiering state).
|
// Do not tier down asm.js (just never change the tiering state).
|
||||||
if (module()->origin != kWasmOrigin) return;
|
if (module()->origin != kWasmOrigin) return;
|
||||||
|
|
||||||
base::RecursiveMutexGuard lock(&allocation_mutex_);
|
base::RecursiveMutexGuard lock(&allocation_mutex_);
|
||||||
debug_state_ = new_debug_state;
|
tiering_state_ = new_tiering_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeModule::IsTieredDown() {
|
||||||
|
base::RecursiveMutexGuard lock(&allocation_mutex_);
|
||||||
|
return tiering_state_ == kTieredDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeModule::RemoveAllCompiledCode() {
|
void NativeModule::RemoveAllCompiledCode() {
|
||||||
@ -2430,6 +2435,75 @@ void NativeModule::RemoveAllCompiledCode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeModule::RecompileForTiering() {
|
||||||
|
// If baseline compilation is not finished yet, we do not tier down now. This
|
||||||
|
// would be tricky because not all code is guaranteed to be available yet.
|
||||||
|
// Instead, we tier down after streaming compilation finished.
|
||||||
|
if (!compilation_state_->baseline_compilation_finished()) return;
|
||||||
|
|
||||||
|
// Read the tiering state under the lock, then trigger recompilation after
|
||||||
|
// releasing the lock. If the tiering state was changed when the triggered
|
||||||
|
// compilation units finish, code installation will handle that correctly.
|
||||||
|
TieringState current_state;
|
||||||
|
{
|
||||||
|
base::RecursiveMutexGuard lock(&allocation_mutex_);
|
||||||
|
current_state = tiering_state_;
|
||||||
|
|
||||||
|
// Initialize {cached_code_} to signal that this cache should get filled
|
||||||
|
// from now on.
|
||||||
|
if (!cached_code_) {
|
||||||
|
cached_code_ = std::make_unique<
|
||||||
|
std::map<std::pair<ExecutionTier, int>, WasmCode*>>();
|
||||||
|
// Fill with existing code.
|
||||||
|
for (auto& code_entry : owned_code_) {
|
||||||
|
InsertToCodeCache(code_entry.second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecompileNativeModule(this, current_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> NativeModule::FindFunctionsToRecompile(
|
||||||
|
TieringState new_tiering_state) {
|
||||||
|
WasmCodeRefScope code_ref_scope;
|
||||||
|
base::RecursiveMutexGuard guard(&allocation_mutex_);
|
||||||
|
// Get writable permission already here (and not inside the loop in
|
||||||
|
// {PatchJumpTablesLocked}), to avoid switching for each slot individually.
|
||||||
|
CodeSpaceWriteScope code_space_write_scope(this);
|
||||||
|
std::vector<int> function_indexes;
|
||||||
|
int imported = module()->num_imported_functions;
|
||||||
|
int declared = module()->num_declared_functions;
|
||||||
|
const bool tier_down = new_tiering_state == kTieredDown;
|
||||||
|
for (int slot_index = 0; slot_index < declared; ++slot_index) {
|
||||||
|
int function_index = imported + slot_index;
|
||||||
|
WasmCode* old_code = code_table_[slot_index];
|
||||||
|
bool code_is_good =
|
||||||
|
tier_down ? old_code && old_code->for_debugging()
|
||||||
|
: old_code && old_code->tier() == ExecutionTier::kTurbofan;
|
||||||
|
if (code_is_good) continue;
|
||||||
|
DCHECK_NOT_NULL(cached_code_);
|
||||||
|
auto cache_it = cached_code_->find(std::make_pair(
|
||||||
|
tier_down ? ExecutionTier::kLiftoff : ExecutionTier::kTurbofan,
|
||||||
|
function_index));
|
||||||
|
if (cache_it != cached_code_->end()) {
|
||||||
|
WasmCode* cached_code = cache_it->second;
|
||||||
|
if (old_code) {
|
||||||
|
WasmCodeRefScope::AddRef(old_code);
|
||||||
|
// The code is added to the current {WasmCodeRefScope}, hence the ref
|
||||||
|
// count cannot drop to zero here.
|
||||||
|
old_code->DecRefOnLiveCode();
|
||||||
|
}
|
||||||
|
code_table_[slot_index] = cached_code;
|
||||||
|
PatchJumpTablesLocked(slot_index, cached_code->instruction_start());
|
||||||
|
cached_code->IncRef();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Otherwise add the function to the set of functions to recompile.
|
||||||
|
function_indexes.push_back(function_index);
|
||||||
|
}
|
||||||
|
return function_indexes;
|
||||||
|
}
|
||||||
|
|
||||||
void NativeModule::FreeCode(base::Vector<WasmCode* const> codes) {
|
void NativeModule::FreeCode(base::Vector<WasmCode* const> codes) {
|
||||||
base::RecursiveMutexGuard guard(&allocation_mutex_);
|
base::RecursiveMutexGuard guard(&allocation_mutex_);
|
||||||
// Free the code space.
|
// Free the code space.
|
||||||
|
@ -830,20 +830,29 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
|||||||
V8_WARN_UNUSED_RESULT std::vector<std::unique_ptr<WasmCode>> AddCompiledCode(
|
V8_WARN_UNUSED_RESULT std::vector<std::unique_ptr<WasmCode>> AddCompiledCode(
|
||||||
base::Vector<WasmCompilationResult>);
|
base::Vector<WasmCompilationResult>);
|
||||||
|
|
||||||
// Set a new debugging state, but don't trigger any recompilation;
|
// Set a new tiering state, but don't trigger any recompilation yet; use
|
||||||
// recompilation happens lazily.
|
// {RecompileForTiering} for that. The two steps are split because In some
|
||||||
void SetDebugState(DebugState);
|
// scenarios we need to drop locks before triggering recompilation.
|
||||||
|
void SetTieringState(TieringState);
|
||||||
|
|
||||||
// Check whether this modules is in debug state.
|
// Check whether this modules is tiered down for debugging.
|
||||||
DebugState IsInDebugState() const {
|
bool IsTieredDown();
|
||||||
base::RecursiveMutexGuard lock(&allocation_mutex_);
|
|
||||||
return debug_state_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all compiled code from the {NativeModule} and replace it with
|
// Remove all compiled code from the {NativeModule} and replace it with
|
||||||
// {CompileLazy} builtins.
|
// {CompileLazy} builtins.
|
||||||
void RemoveAllCompiledCode();
|
void RemoveAllCompiledCode();
|
||||||
|
|
||||||
|
// Fully recompile this module in the tier set previously via
|
||||||
|
// {SetTieringState}. The calling thread contributes to compilation and only
|
||||||
|
// returns once recompilation is done.
|
||||||
|
void RecompileForTiering();
|
||||||
|
|
||||||
|
// Find all functions that need to be recompiled for a new tier. Note that
|
||||||
|
// compilation jobs might run concurrently, so this method only considers the
|
||||||
|
// compilation state of this native module at the time of the call.
|
||||||
|
// Returns a vector of function indexes to recompile.
|
||||||
|
std::vector<int> FindFunctionsToRecompile(TieringState);
|
||||||
|
|
||||||
// Free a set of functions of this module. Uncommits whole pages if possible.
|
// Free a set of functions of this module. Uncommits whole pages if possible.
|
||||||
// The given vector must be ordered by the instruction start address, and all
|
// The given vector must be ordered by the instruction start address, and all
|
||||||
// {WasmCode} objects must not be used any more.
|
// {WasmCode} objects must not be used any more.
|
||||||
@ -1010,7 +1019,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
|||||||
|
|
||||||
std::unique_ptr<NamesProvider> names_provider_;
|
std::unique_ptr<NamesProvider> names_provider_;
|
||||||
|
|
||||||
DebugState debug_state_ = kNotDebugging;
|
TieringState tiering_state_ = kTieredUp;
|
||||||
|
|
||||||
// Cache both baseline and top-tier code if we are debugging, to speed up
|
// Cache both baseline and top-tier code if we are debugging, to speed up
|
||||||
// repeated enabling/disabling of the debugger or profiler.
|
// repeated enabling/disabling of the debugger or profiler.
|
||||||
|
@ -390,8 +390,8 @@ struct WasmEngine::IsolateInfo {
|
|||||||
|
|
||||||
const std::shared_ptr<Counters> async_counters;
|
const std::shared_ptr<Counters> async_counters;
|
||||||
|
|
||||||
// Keep new modules in debug state.
|
// Keep new modules in tiered down state.
|
||||||
bool keep_in_debug_state = false;
|
bool keep_tiered_down = false;
|
||||||
|
|
||||||
// Keep track whether we already added a sample for PKU support (we only want
|
// Keep track whether we already added a sample for PKU support (we only want
|
||||||
// one sample per Isolate).
|
// one sample per Isolate).
|
||||||
@ -714,14 +714,14 @@ void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
|
|||||||
&native_module->module()->functions[function_index], tier);
|
&native_module->module()->functions[function_index], tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WasmEngine::EnterDebuggingForIsolate(Isolate* isolate) {
|
void WasmEngine::TierDownAllModulesPerIsolate(Isolate* isolate) {
|
||||||
std::vector<std::shared_ptr<NativeModule>> native_modules;
|
std::vector<std::shared_ptr<NativeModule>> native_modules;
|
||||||
{
|
{
|
||||||
base::MutexGuard lock(&mutex_);
|
base::MutexGuard lock(&mutex_);
|
||||||
if (isolates_[isolate]->keep_in_debug_state) return;
|
if (isolates_[isolate]->keep_tiered_down) return;
|
||||||
isolates_[isolate]->keep_in_debug_state = true;
|
isolates_[isolate]->keep_tiered_down = true;
|
||||||
for (auto* native_module : isolates_[isolate]->native_modules) {
|
for (auto* native_module : isolates_[isolate]->native_modules) {
|
||||||
native_module->SetDebugState(kDebugging);
|
native_module->SetTieringState(kTieredDown);
|
||||||
DCHECK_EQ(1, native_modules_.count(native_module));
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
||||||
if (auto shared_ptr = native_modules_[native_module]->weak_ptr.lock()) {
|
if (auto shared_ptr = native_modules_[native_module]->weak_ptr.lock()) {
|
||||||
native_modules.emplace_back(std::move(shared_ptr));
|
native_modules.emplace_back(std::move(shared_ptr));
|
||||||
@ -729,7 +729,7 @@ void WasmEngine::EnterDebuggingForIsolate(Isolate* isolate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& native_module : native_modules) {
|
for (auto& native_module : native_modules) {
|
||||||
native_module->RemoveAllCompiledCode();
|
native_module->RecompileForTiering();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,12 +740,12 @@ void WasmEngine::LeaveDebuggingForIsolate(Isolate* isolate) {
|
|||||||
std::vector<std::pair<std::shared_ptr<NativeModule>, bool>> native_modules;
|
std::vector<std::pair<std::shared_ptr<NativeModule>, bool>> native_modules;
|
||||||
{
|
{
|
||||||
base::MutexGuard lock(&mutex_);
|
base::MutexGuard lock(&mutex_);
|
||||||
isolates_[isolate]->keep_in_debug_state = false;
|
isolates_[isolate]->keep_tiered_down = false;
|
||||||
auto can_remove_debug_code = [this](NativeModule* native_module) {
|
auto can_remove_debug_code = [this](NativeModule* native_module) {
|
||||||
DCHECK_EQ(1, native_modules_.count(native_module));
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
||||||
for (auto* isolate : native_modules_[native_module]->isolates) {
|
for (auto* isolate : native_modules_[native_module]->isolates) {
|
||||||
DCHECK_EQ(1, isolates_.count(isolate));
|
DCHECK_EQ(1, isolates_.count(isolate));
|
||||||
if (isolates_[isolate]->keep_in_debug_state) return false;
|
if (isolates_[isolate]->keep_tiered_down) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -753,11 +753,11 @@ void WasmEngine::LeaveDebuggingForIsolate(Isolate* isolate) {
|
|||||||
DCHECK_EQ(1, native_modules_.count(native_module));
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
||||||
auto shared_ptr = native_modules_[native_module]->weak_ptr.lock();
|
auto shared_ptr = native_modules_[native_module]->weak_ptr.lock();
|
||||||
if (!shared_ptr) continue; // The module is not used any more.
|
if (!shared_ptr) continue; // The module is not used any more.
|
||||||
if (!native_module->IsInDebugState()) continue;
|
if (!native_module->IsTieredDown()) continue;
|
||||||
// Only start tier-up if no other isolate needs this module in tiered
|
// Only start tier-up if no other isolate needs this module in tiered
|
||||||
// down state.
|
// down state.
|
||||||
bool remove_debug_code = can_remove_debug_code(native_module);
|
bool remove_debug_code = can_remove_debug_code(native_module);
|
||||||
if (remove_debug_code) native_module->SetDebugState(kNotDebugging);
|
if (remove_debug_code) native_module->SetTieringState(kTieredUp);
|
||||||
native_modules.emplace_back(std::move(shared_ptr), remove_debug_code);
|
native_modules.emplace_back(std::move(shared_ptr), remove_debug_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1205,8 +1205,8 @@ std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
|
|||||||
pair.first->second.get()->isolates.insert(isolate);
|
pair.first->second.get()->isolates.insert(isolate);
|
||||||
auto* isolate_info = isolates_[isolate].get();
|
auto* isolate_info = isolates_[isolate].get();
|
||||||
isolate_info->native_modules.insert(native_module.get());
|
isolate_info->native_modules.insert(native_module.get());
|
||||||
if (isolate_info->keep_in_debug_state) {
|
if (isolate_info->keep_tiered_down) {
|
||||||
native_module->SetDebugState(kDebugging);
|
native_module->SetTieringState(kTieredDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record memory protection key support.
|
// Record memory protection key support.
|
||||||
@ -1232,7 +1232,7 @@ std::shared_ptr<NativeModule> WasmEngine::MaybeGetNativeModule(
|
|||||||
wire_bytes.size());
|
wire_bytes.size());
|
||||||
std::shared_ptr<NativeModule> native_module =
|
std::shared_ptr<NativeModule> native_module =
|
||||||
native_module_cache_.MaybeGetNativeModule(origin, wire_bytes);
|
native_module_cache_.MaybeGetNativeModule(origin, wire_bytes);
|
||||||
bool remove_all_code = false;
|
bool recompile_module = false;
|
||||||
if (native_module) {
|
if (native_module) {
|
||||||
TRACE_EVENT0("v8.wasm", "CacheHit");
|
TRACE_EVENT0("v8.wasm", "CacheHit");
|
||||||
base::MutexGuard guard(&mutex_);
|
base::MutexGuard guard(&mutex_);
|
||||||
@ -1242,12 +1242,13 @@ std::shared_ptr<NativeModule> WasmEngine::MaybeGetNativeModule(
|
|||||||
}
|
}
|
||||||
native_module_info->isolates.insert(isolate);
|
native_module_info->isolates.insert(isolate);
|
||||||
isolates_[isolate]->native_modules.insert(native_module.get());
|
isolates_[isolate]->native_modules.insert(native_module.get());
|
||||||
if (isolates_[isolate]->keep_in_debug_state) {
|
if (isolates_[isolate]->keep_tiered_down) {
|
||||||
native_module->SetDebugState(kDebugging);
|
native_module->SetTieringState(kTieredDown);
|
||||||
remove_all_code = true;
|
recompile_module = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (remove_all_code) native_module->RemoveAllCompiledCode();
|
// Potentially recompile the module for tier down, after releasing the mutex.
|
||||||
|
if (recompile_module) native_module->RecompileForTiering();
|
||||||
return native_module;
|
return native_module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1260,19 +1261,21 @@ std::shared_ptr<NativeModule> WasmEngine::UpdateNativeModuleCache(
|
|||||||
native_module =
|
native_module =
|
||||||
native_module_cache_.Update(std::move(native_module), has_error);
|
native_module_cache_.Update(std::move(native_module), has_error);
|
||||||
if (prev == native_module.get()) return native_module;
|
if (prev == native_module.get()) return native_module;
|
||||||
bool remove_all_code = false;
|
|
||||||
|
bool recompile_module = false;
|
||||||
{
|
{
|
||||||
base::MutexGuard guard(&mutex_);
|
base::MutexGuard guard(&mutex_);
|
||||||
DCHECK_EQ(1, native_modules_.count(native_module.get()));
|
DCHECK_EQ(1, native_modules_.count(native_module.get()));
|
||||||
native_modules_[native_module.get()]->isolates.insert(isolate);
|
native_modules_[native_module.get()]->isolates.insert(isolate);
|
||||||
DCHECK_EQ(1, isolates_.count(isolate));
|
DCHECK_EQ(1, isolates_.count(isolate));
|
||||||
isolates_[isolate]->native_modules.insert(native_module.get());
|
isolates_[isolate]->native_modules.insert(native_module.get());
|
||||||
if (isolates_[isolate]->keep_in_debug_state) {
|
if (isolates_[isolate]->keep_tiered_down) {
|
||||||
remove_all_code = true;
|
native_module->SetTieringState(kTieredDown);
|
||||||
native_module->SetDebugState(kDebugging);
|
recompile_module = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (remove_all_code) native_module->RemoveAllCompiledCode();
|
// Potentially recompile the module for tier down, after releasing the mutex.
|
||||||
|
if (recompile_module) native_module->RecompileForTiering();
|
||||||
return native_module;
|
return native_module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
|
|||||||
void CompileFunction(Isolate* isolate, NativeModule* native_module,
|
void CompileFunction(Isolate* isolate, NativeModule* native_module,
|
||||||
uint32_t function_index, ExecutionTier tier);
|
uint32_t function_index, ExecutionTier tier);
|
||||||
|
|
||||||
void EnterDebuggingForIsolate(Isolate* isolate);
|
void TierDownAllModulesPerIsolate(Isolate* isolate);
|
||||||
|
|
||||||
void LeaveDebuggingForIsolate(Isolate* isolate);
|
void LeaveDebuggingForIsolate(Isolate* isolate);
|
||||||
|
|
||||||
|
@ -1446,7 +1446,7 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
|
|||||||
result.tagged_parameter_slots,
|
result.tagged_parameter_slots,
|
||||||
result.protected_instructions_data.as_vector(),
|
result.protected_instructions_data.as_vector(),
|
||||||
result.source_positions.as_vector(), GetCodeKind(result),
|
result.source_positions.as_vector(), GetCodeKind(result),
|
||||||
wasm::ExecutionTier::kNone, wasm::kNotForDebugging);
|
wasm::ExecutionTier::kNone, wasm::kNoDebugging);
|
||||||
wasm::WasmCode* published_code =
|
wasm::WasmCode* published_code =
|
||||||
native_module->PublishCode(std::move(wasm_code));
|
native_module->PublishCode(std::move(wasm_code));
|
||||||
isolate->counters()->wasm_generated_code_size()->Increment(
|
isolate->counters()->wasm_generated_code_size()->Increment(
|
||||||
|
@ -907,7 +907,7 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule(
|
|||||||
isolate, enabled_features, std::move(module), code_size_estimate);
|
isolate, enabled_features, std::move(module), code_size_estimate);
|
||||||
// We have to assign a compilation ID here, as it is required for a
|
// We have to assign a compilation ID here, as it is required for a
|
||||||
// potential re-compilation, e.g. triggered by
|
// potential re-compilation, e.g. triggered by
|
||||||
// {EnterDebuggingForIsolate}. The value is -2 so that it is different
|
// {TierDownAllModulesPerIsolate}. The value is -2 so that it is different
|
||||||
// than the compilation ID of actual compilations, and also different than
|
// than the compilation ID of actual compilations, and also different than
|
||||||
// the sentinel value of the CompilationState.
|
// the sentinel value of the CompilationState.
|
||||||
shared_native_module->compilation_state()->set_compilation_id(-2);
|
shared_native_module->compilation_state()->set_compilation_id(-2);
|
||||||
|
@ -37,13 +37,13 @@ inline const char* ExecutionTierToString(ExecutionTier tier) {
|
|||||||
// the code also contains breakpoints, and {kForStepping} for code that is
|
// the code also contains breakpoints, and {kForStepping} for code that is
|
||||||
// flooded with breakpoints.
|
// flooded with breakpoints.
|
||||||
enum ForDebugging : int8_t {
|
enum ForDebugging : int8_t {
|
||||||
kNotForDebugging = 0,
|
kNoDebugging = 0,
|
||||||
kForDebugging,
|
kForDebugging,
|
||||||
kWithBreakpoints,
|
kWithBreakpoints,
|
||||||
kForStepping
|
kForStepping
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DebugState : bool { kNotDebugging = false, kDebugging = true };
|
enum TieringState : int8_t { kTieredUp, kTieredDown };
|
||||||
|
|
||||||
} // namespace wasm
|
} // namespace wasm
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -26,7 +26,7 @@ class LiftoffCompileEnvironment {
|
|||||||
// Add a table of length 1, for indirect calls.
|
// Add a table of length 1, for indirect calls.
|
||||||
wasm_runner_.builder().AddIndirectFunctionTable(nullptr, 1);
|
wasm_runner_.builder().AddIndirectFunctionTable(nullptr, 1);
|
||||||
// Set tiered down such that we generate debugging code.
|
// Set tiered down such that we generate debugging code.
|
||||||
wasm_runner_.builder().SetDebugState();
|
wasm_runner_.builder().SetTieredDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestFunction {
|
struct TestFunction {
|
||||||
|
@ -3954,7 +3954,7 @@ TEST(Liftoff_tier_up) {
|
|||||||
CodeSpaceWriteScope write_scope(native_module);
|
CodeSpaceWriteScope write_scope(native_module);
|
||||||
std::unique_ptr<WasmCode> new_code = native_module->AddCode(
|
std::unique_ptr<WasmCode> new_code = native_module->AddCode(
|
||||||
add.function_index(), desc, 0, 0, {}, {}, WasmCode::kWasmFunction,
|
add.function_index(), desc, 0, 0, {}, {}, WasmCode::kWasmFunction,
|
||||||
ExecutionTier::kTurbofan, kNotForDebugging);
|
ExecutionTier::kTurbofan, kNoDebugging);
|
||||||
native_module->PublishCode(std::move(new_code));
|
native_module->PublishCode(std::move(new_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1601,7 +1601,7 @@ STREAM_TEST(TierDownWithError) {
|
|||||||
builder.WriteTo(&buffer);
|
builder.WriteTo(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetWasmEngine()->EnterDebuggingForIsolate(i_isolate);
|
GetWasmEngine()->TierDownAllModulesPerIsolate(i_isolate);
|
||||||
|
|
||||||
tester.OnBytesReceived(buffer.begin(), buffer.size());
|
tester.OnBytesReceived(buffer.begin(), buffer.size());
|
||||||
tester.FinishStream();
|
tester.FinishStream();
|
||||||
|
@ -140,7 +140,7 @@ class BreakHandler : public debug::DebugDelegate {
|
|||||||
Handle<BreakPoint> SetBreakpoint(WasmRunnerBase* runner, int function_index,
|
Handle<BreakPoint> SetBreakpoint(WasmRunnerBase* runner, int function_index,
|
||||||
int byte_offset,
|
int byte_offset,
|
||||||
int expected_set_byte_offset = -1) {
|
int expected_set_byte_offset = -1) {
|
||||||
runner->SwitchToDebug();
|
runner->TierDown();
|
||||||
int func_offset =
|
int func_offset =
|
||||||
runner->builder().GetFunctionAt(function_index)->code.offset();
|
runner->builder().GetFunctionAt(function_index)->code.offset();
|
||||||
int code_offset = func_offset + byte_offset;
|
int code_offset = func_offset + byte_offset;
|
||||||
@ -370,7 +370,7 @@ WASM_COMPILED_EXEC_TEST(WasmSimpleStepping) {
|
|||||||
|
|
||||||
WASM_COMPILED_EXEC_TEST(WasmStepInAndOut) {
|
WASM_COMPILED_EXEC_TEST(WasmStepInAndOut) {
|
||||||
WasmRunner<int, int> runner(execution_tier);
|
WasmRunner<int, int> runner(execution_tier);
|
||||||
runner.SwitchToDebug();
|
runner.TierDown();
|
||||||
WasmFunctionCompiler& f2 = runner.NewFunction<void>();
|
WasmFunctionCompiler& f2 = runner.NewFunction<void>();
|
||||||
f2.AllocateLocal(kWasmI32);
|
f2.AllocateLocal(kWasmI32);
|
||||||
|
|
||||||
|
@ -342,11 +342,10 @@ TEST(TierDownAfterDeserialization) {
|
|||||||
CHECK_NOT_NULL(turbofan_code);
|
CHECK_NOT_NULL(turbofan_code);
|
||||||
CHECK_EQ(ExecutionTier::kTurbofan, turbofan_code->tier());
|
CHECK_EQ(ExecutionTier::kTurbofan, turbofan_code->tier());
|
||||||
|
|
||||||
GetWasmEngine()->EnterDebuggingForIsolate(isolate);
|
GetWasmEngine()->TierDownAllModulesPerIsolate(isolate);
|
||||||
|
|
||||||
// Entering debugging should delete all code, so that debug code gets compiled
|
auto* liftoff_code = native_module->GetCode(0);
|
||||||
// lazily.
|
CHECK_EQ(ExecutionTier::kLiftoff, liftoff_code->tier());
|
||||||
CHECK_NULL(native_module->GetCode(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SerializeLiftoffModuleFails) {
|
TEST(SerializeLiftoffModuleFails) {
|
||||||
|
@ -611,7 +611,7 @@ void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
|
|||||||
NativeModule* native_module =
|
NativeModule* native_module =
|
||||||
builder_->instance_object()->module_object().native_module();
|
builder_->instance_object()->module_object().native_module();
|
||||||
ForDebugging for_debugging =
|
ForDebugging for_debugging =
|
||||||
native_module->IsInDebugState() ? kForDebugging : kNotForDebugging;
|
native_module->IsTieredDown() ? kForDebugging : kNoDebugging;
|
||||||
|
|
||||||
base::Optional<WasmCompilationResult> result;
|
base::Optional<WasmCompilationResult> result;
|
||||||
if (builder_->test_execution_tier() ==
|
if (builder_->test_execution_tier() ==
|
||||||
|
@ -244,14 +244,14 @@ class TestingModuleBuilder {
|
|||||||
return reinterpret_cast<Address>(globals_data_);
|
return reinterpret_cast<Address>(globals_data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDebugState() {
|
void SetTieredDown() {
|
||||||
native_module_->SetDebugState(kDebugging);
|
native_module_->SetTieringState(kTieredDown);
|
||||||
execution_tier_ = TestExecutionTier::kLiftoff;
|
execution_tier_ = TestExecutionTier::kLiftoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitchToDebug() {
|
void TierDown() {
|
||||||
SetDebugState();
|
SetTieredDown();
|
||||||
native_module_->RemoveAllCompiledCode();
|
native_module_->RecompileForTiering();
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationEnv CreateCompilationEnv();
|
CompilationEnv CreateCompilationEnv();
|
||||||
@ -471,7 +471,7 @@ class WasmRunnerBase : public InitializedHandleScope {
|
|||||||
|
|
||||||
bool interpret() { return builder_.interpret(); }
|
bool interpret() { return builder_.interpret(); }
|
||||||
|
|
||||||
void SwitchToDebug() { builder_.SwitchToDebug(); }
|
void TierDown() { builder_.TierDown(); }
|
||||||
|
|
||||||
template <typename ReturnType, typename... ParamTypes>
|
template <typename ReturnType, typename... ParamTypes>
|
||||||
FunctionSig* CreateSig() {
|
FunctionSig* CreateSig() {
|
||||||
|
@ -17,21 +17,19 @@ function create_builder(delta = 0) {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDebugCode(instance) {
|
function checkTieredDown(instance) {
|
||||||
for (let i = 0; i < num_functions; ++i) {
|
for (let i = 0; i < num_functions; ++i) {
|
||||||
// Call the function once because of lazy compilation.
|
assertTrue(%IsLiftoffFunction(instance.exports['f' + i]));
|
||||||
instance.exports['f' + i]();
|
|
||||||
assertTrue(%IsWasmDebugFunction(instance.exports['f' + i]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForNoDebugCode(instance) {
|
function waitForTieredUp(instance) {
|
||||||
// Busy waiting until all functions left debug mode.
|
// Busy waiting until all functions are tiered up.
|
||||||
let num_liftoff_functions = 0;
|
let num_liftoff_functions = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
num_liftoff_functions = 0;
|
num_liftoff_functions = 0;
|
||||||
for (let i = 0; i < num_functions; ++i) {
|
for (let i = 0; i < num_functions; ++i) {
|
||||||
if (%IsWasmDebugFunction(instance.exports['f' + i])) {
|
if (%IsLiftoffFunction(instance.exports['f' + i])) {
|
||||||
num_liftoff_functions++;
|
num_liftoff_functions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,37 +39,37 @@ function waitForNoDebugCode(instance) {
|
|||||||
|
|
||||||
const Debug = new DebugWrapper();
|
const Debug = new DebugWrapper();
|
||||||
|
|
||||||
(function testEnterDebugMode() {
|
(function testTierDownToLiftoff() {
|
||||||
// In the 'isolates' test, this test runs in parallel to itself on two
|
// In the 'isolates' test, this test runs in parallel to itself on two
|
||||||
// isolates. All checks below should still hold.
|
// isolates. All checks below should still hold.
|
||||||
const instance = create_builder(0).instantiate();
|
const instance = create_builder(0).instantiate();
|
||||||
Debug.enable();
|
Debug.enable();
|
||||||
checkDebugCode(instance);
|
checkTieredDown(instance);
|
||||||
const instance2 = create_builder(1).instantiate();
|
const instance2 = create_builder(1).instantiate();
|
||||||
checkDebugCode(instance2);
|
checkTieredDown(instance2);
|
||||||
Debug.disable();
|
Debug.disable();
|
||||||
// Eventually the instances will have completely left debug mode again.
|
// Eventually the instances will be completely tiered up again.
|
||||||
waitForNoDebugCode(instance);
|
waitForTieredUp(instance);
|
||||||
waitForNoDebugCode(instance2);
|
waitForTieredUp(instance2);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Test async compilation.
|
// Test async compilation.
|
||||||
assertPromiseResult((async function testEnterDebugModeAsync() {
|
assertPromiseResult((async function testTierDownToLiftoffAsync() {
|
||||||
// First test: enable the debugger *after* compiling the module.
|
// First test: enable the debugger *after* compiling the module.
|
||||||
const instance = await create_builder(2).asyncInstantiate();
|
const instance = await create_builder(2).asyncInstantiate();
|
||||||
Debug.enable();
|
Debug.enable();
|
||||||
checkDebugCode(instance);
|
checkTieredDown(instance);
|
||||||
const instance2 = await create_builder(3).asyncInstantiate();
|
const instance2 = await create_builder(3).asyncInstantiate();
|
||||||
checkDebugCode(instance2);
|
checkTieredDown(instance2);
|
||||||
Debug.disable();
|
Debug.disable();
|
||||||
waitForNoDebugCode(instance);
|
waitForTieredUp(instance);
|
||||||
waitForNoDebugCode(instance2);
|
waitForTieredUp(instance2);
|
||||||
|
|
||||||
// Second test: enable the debugger *while* compiling the module.
|
// Second test: enable the debugger *while* compiling the module.
|
||||||
const instancePromise = create_builder(4).asyncInstantiate();
|
const instancePromise = create_builder(4).asyncInstantiate();
|
||||||
Debug.enable();
|
Debug.enable();
|
||||||
const instance3 = await instancePromise;
|
const instance3 = await instancePromise;
|
||||||
checkDebugCode(instance3);
|
checkTieredDown(instance3);
|
||||||
Debug.disable();
|
Debug.disable();
|
||||||
waitForNoDebugCode(instance3);
|
waitForTieredUp(instance3);
|
||||||
})());
|
})());
|
||||||
|
@ -1333,7 +1333,7 @@
|
|||||||
'wasm/liftoff': [SKIP],
|
'wasm/liftoff': [SKIP],
|
||||||
'wasm/liftoff-debug': [SKIP],
|
'wasm/liftoff-debug': [SKIP],
|
||||||
'wasm/tier-up-testing-flag': [SKIP],
|
'wasm/tier-up-testing-flag': [SKIP],
|
||||||
'wasm/enter-debug-state': [SKIP],
|
'wasm/tier-down-to-liftoff': [SKIP],
|
||||||
'wasm/wasm-dynamic-tiering': [SKIP],
|
'wasm/wasm-dynamic-tiering': [SKIP],
|
||||||
'wasm/test-partial-serialization': [SKIP],
|
'wasm/test-partial-serialization': [SKIP],
|
||||||
'regress/wasm/regress-1248024': [SKIP],
|
'regress/wasm/regress-1248024': [SKIP],
|
||||||
|
@ -18,22 +18,20 @@ function create_builder(delta = 0) {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForDebugCode(instance) {
|
function checkTieredDown(instance) {
|
||||||
for (let i = 0; i < num_functions; ++i) {
|
for (let i = 0; i < num_functions; ++i) {
|
||||||
// Call the function once because of lazy compilation.
|
assertTrue(%IsLiftoffFunction(instance.exports['f' + i]));
|
||||||
instance.exports['f' + i]();
|
|
||||||
assertTrue(%IsWasmDebugFunction(instance.exports['f' + i]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function check(instance) {
|
function check(instance) {
|
||||||
%WasmEnterDebugging();
|
%WasmTierDown();
|
||||||
checkForDebugCode(instance);
|
checkTieredDown(instance);
|
||||||
|
|
||||||
for (let i = 0; i < num_functions; ++i) {
|
for (let i = 0; i < num_functions; ++i) {
|
||||||
%WasmTierUpFunction(instance, i);
|
%WasmTierUpFunction(instance, i);
|
||||||
}
|
}
|
||||||
checkForDebugCode(instance);
|
checkTieredDown(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
(function testTierDownToLiftoff() {
|
(function testTierDownToLiftoff() {
|
Loading…
Reference in New Issue
Block a user