summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/module-compiler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/module-compiler.cc')
-rw-r--r--deps/v8/src/wasm/module-compiler.cc201
1 files changed, 130 insertions, 71 deletions
diff --git a/deps/v8/src/wasm/module-compiler.cc b/deps/v8/src/wasm/module-compiler.cc
index 0a692e7bcd..af7551e535 100644
--- a/deps/v8/src/wasm/module-compiler.cc
+++ b/deps/v8/src/wasm/module-compiler.cc
@@ -17,7 +17,7 @@
#include "src/base/utils/random-number-generator.h"
#include "src/compiler/wasm-compiler.h"
#include "src/handles/global-handles-inl.h"
-#include "src/heap/heap-inl.h" // For CodeSpaceMemoryModificationScope.
+#include "src/heap/heap-inl.h" // For CodePageCollectionMemoryModificationScope.
#include "src/logging/counters-scopes.h"
#include "src/logging/metrics.h"
#include "src/objects/property-descriptor.h"
@@ -572,14 +572,14 @@ class CompilationStateImpl {
// 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,
- CompilationState::callback_t recompilation_finished_callback);
+ void InitializeRecompilation(TieringState new_tiering_state,
+ std::unique_ptr<CompilationEventCallback>
+ recompilation_finished_callback);
- // Add the callback function 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
// events. The callback object must support being deleted from any thread.
- void AddCallback(CompilationState::callback_t);
+ void AddCallback(std::unique_ptr<CompilationEventCallback> callback);
// Inserts new functions to compile and kicks off compilation.
void CommitCompilationUnits(
@@ -744,8 +744,8 @@ class CompilationStateImpl {
//////////////////////////////////////////////////////////////////////////////
// Protected by {callbacks_mutex_}:
- // Callback functions to be called on compilation events.
- std::vector<CompilationState::callback_t> callbacks_;
+ // Callbacks to be called on compilation events.
+ std::vector<std::unique_ptr<CompilationEventCallback>> callbacks_;
// Events that already happened.
base::EnumSet<CompilationEvent> finished_events_;
@@ -836,7 +836,8 @@ std::shared_ptr<WireBytesStorage> CompilationState::GetWireBytesStorage()
return Impl(this)->GetWireBytesStorage();
}
-void CompilationState::AddCallback(CompilationState::callback_t callback) {
+void CompilationState::AddCallback(
+ std::unique_ptr<CompilationEventCallback> callback) {
return Impl(this)->AddCallback(std::move(callback));
}
@@ -1252,19 +1253,22 @@ std::vector<CallSiteFeedback> ProcessTypeFeedback(
static_cast<int>(instance->module()->num_imported_functions);
for (int i = 0; i < feedback.length(); i += 2) {
Object value = feedback.get(i);
- if (WasmExportedFunction::IsWasmExportedFunction(value)) {
- // Monomorphic. Mark the target for inlining if it's defined in the
- // same module.
- WasmExportedFunction target = WasmExportedFunction::cast(value);
+ if (value.IsWasmInternalFunction() &&
+ WasmExportedFunction::IsWasmExportedFunction(
+ WasmInternalFunction::cast(value).external())) {
+ // Monomorphic, and the internal function points to a wasm-generated
+ // external function (WasmExportedFunction). Mark the target for inlining
+ // if it's defined in the same module.
+ WasmExportedFunction target = WasmExportedFunction::cast(
+ WasmInternalFunction::cast(value).external());
if (target.instance() == *instance &&
target.function_index() >= imported_functions) {
if (FLAG_trace_wasm_speculative_inlining) {
PrintF("[Function #%d call_ref #%d inlineable (monomorphic)]\n",
func_index, i / 2);
}
- CallRefData data = CallRefData::cast(feedback.get(i + 1));
- result[i / 2] = {target.function_index(),
- static_cast<int>(data.count())};
+ int32_t count = Smi::cast(feedback.get(i + 1)).value();
+ result[i / 2] = {target.function_index(), count};
continue;
}
} else if (value.IsFixedArray()) {
@@ -1274,26 +1278,35 @@ std::vector<CallSiteFeedback> ProcessTypeFeedback(
FixedArray polymorphic = FixedArray::cast(value);
size_t total_count = 0;
for (int j = 0; j < polymorphic.length(); j += 2) {
- total_count += CallRefData::cast(polymorphic.get(j + 1)).count();
+ total_count += Smi::cast(polymorphic.get(j + 1)).value();
}
int found_target = -1;
int found_count = -1;
double best_frequency = 0;
for (int j = 0; j < polymorphic.length(); j += 2) {
- uint32_t this_count = CallRefData::cast(polymorphic.get(j + 1)).count();
+ int32_t this_count = Smi::cast(polymorphic.get(j + 1)).value();
double frequency = static_cast<double>(this_count) / total_count;
if (frequency > best_frequency) best_frequency = frequency;
if (frequency < 0.8) continue;
- Object maybe_target = polymorphic.get(j);
- if (!WasmExportedFunction::IsWasmExportedFunction(maybe_target)) {
+
+ // We reject this polymorphic entry if:
+ // - it is not defined,
+ // - it is not a wasm-defined function (WasmExportedFunction)
+ // - it was not defined in this module.
+ if (!polymorphic.get(j).IsWasmInternalFunction()) continue;
+ WasmInternalFunction internal =
+ WasmInternalFunction::cast(polymorphic.get(j));
+ if (!WasmExportedFunction::IsWasmExportedFunction(
+ internal.external())) {
continue;
}
WasmExportedFunction target =
- WasmExportedFunction::cast(polymorphic.get(j));
+ WasmExportedFunction::cast(internal.external());
if (target.instance() != *instance ||
target.function_index() < imported_functions) {
continue;
}
+
found_target = target.function_index();
found_count = static_cast<int>(this_count);
if (FLAG_trace_wasm_speculative_inlining) {
@@ -1313,6 +1326,10 @@ std::vector<CallSiteFeedback> ProcessTypeFeedback(
// If we fall through to here, then this call isn't eligible for inlining.
// Possible reasons: uninitialized or megamorphic feedback; or monomorphic
// or polymorphic that didn't meet our requirements.
+ if (FLAG_trace_wasm_speculative_inlining) {
+ PrintF("[Function #%d call_ref #%d *not* inlineable]\n", func_index,
+ i / 2);
+ }
result[i / 2] = {-1, -1};
}
return result;
@@ -1327,7 +1344,7 @@ void TriggerTierUp(Isolate* isolate, NativeModule* native_module,
const WasmModule* module = native_module->module();
size_t priority;
- if (FLAG_new_wasm_dynamic_tiering) {
+ {
base::MutexGuard mutex_guard(&module->type_feedback.mutex);
int saved_priority =
module->type_feedback.feedback_for_function[func_index].tierup_priority;
@@ -1353,11 +1370,6 @@ void TriggerTierUp(Isolate* isolate, NativeModule* native_module,
std::move(feedback);
}
- if (!FLAG_new_wasm_dynamic_tiering) {
- uint32_t* call_array = native_module->num_liftoff_function_calls_array();
- int offset = wasm::declared_function_index(module, func_index);
- priority = base::Relaxed_Load(reinterpret_cast<int*>(&call_array[offset]));
- }
compilation_state->AddTopTierPriorityCompilationUnit(tiering_unit, priority);
}
@@ -1651,7 +1663,7 @@ bool MayCompriseLazyFunctions(const WasmModule* module,
return false;
}
-class CompilationTimeCallback {
+class CompilationTimeCallback : public CompilationEventCallback {
public:
enum CompileMode { kSynchronous, kAsync, kStreaming };
explicit CompilationTimeCallback(
@@ -1666,7 +1678,12 @@ class CompilationTimeCallback {
native_module_(std::move(native_module)),
compile_mode_(compile_mode) {}
- void operator()(CompilationEvent compilation_event) {
+ // Keep this callback alive to be able to record caching metrics.
+ ReleaseAfterFinalEvent release_after_final_event() override {
+ return CompilationEventCallback::ReleaseAfterFinalEvent::kKeep;
+ }
+
+ void call(CompilationEvent compilation_event) override {
DCHECK(base::TimeTicks::IsHighResolution());
std::shared_ptr<NativeModule> native_module = native_module_.lock();
if (!native_module) return;
@@ -1761,9 +1778,9 @@ void CompileNativeModule(Isolate* isolate,
// The callback captures a shared ptr to the semaphore.
auto* compilation_state = Impl(native_module->compilation_state());
if (base::TimeTicks::IsHighResolution()) {
- compilation_state->AddCallback(CompilationTimeCallback{
+ compilation_state->AddCallback(std::make_unique<CompilationTimeCallback>(
isolate->async_counters(), isolate->metrics_recorder(), context_id,
- native_module, CompilationTimeCallback::kSynchronous});
+ native_module, CompilationTimeCallback::kSynchronous));
}
// Initialize the compilation units and kick off background compile tasks.
@@ -1835,7 +1852,8 @@ class BackgroundCompileJob final : public JobTask {
std::shared_ptr<NativeModule> CompileToNativeModule(
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
- Handle<FixedArray>* export_wrappers_out, int compilation_id) {
+ Handle<FixedArray>* export_wrappers_out, int compilation_id,
+ v8::metrics::Recorder::ContextId context_id) {
const WasmModule* wasm_module = module.get();
WasmEngine* engine = GetWasmEngine();
base::OwnedVector<uint8_t> wire_bytes_copy =
@@ -1872,8 +1890,6 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
// Sync compilation is user blocking, so we increase the priority.
native_module->compilation_state()->SetHighPriority();
- v8::metrics::Recorder::ContextId context_id =
- isolate->GetOrRegisterRecorderContextId(isolate->native_context());
CompileNativeModule(isolate, context_id, thrower, wasm_module, native_module,
export_wrappers_out);
bool cache_hit = !engine->UpdateNativeModuleCache(thrower->error(),
@@ -1897,16 +1913,29 @@ void RecompileNativeModule(NativeModule* native_module,
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,
- [recompilation_finished_semaphore](CompilationEvent event) {
- DCHECK_NE(CompilationEvent::kFailedCompilation, event);
- if (event == CompilationEvent::kFinishedRecompilation) {
- recompilation_finished_semaphore->Signal();
- }
- });
+ tiering_state, std::make_unique<RecompilationFinishedCallback>(
+ recompilation_finished_semaphore));
constexpr JobDelegate* kNoDelegate = nullptr;
ExecuteCompilationUnits(compilation_state->native_module_weak(),
@@ -2204,11 +2233,12 @@ void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
resolver_->OnCompilationSucceeded(result);
}
-class AsyncCompileJob::CompilationStateCallback {
+class AsyncCompileJob::CompilationStateCallback
+ : public CompilationEventCallback {
public:
explicit CompilationStateCallback(AsyncCompileJob* job) : job_(job) {}
- void operator()(CompilationEvent event) {
+ void call(CompilationEvent event) override {
// This callback is only being called from a foreground task.
switch (event) {
case CompilationEvent::kFinishedExportWrappers:
@@ -2521,14 +2551,15 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
CompilationStateImpl* compilation_state =
Impl(job->native_module_->compilation_state());
- compilation_state->AddCallback(CompilationStateCallback{job});
+ compilation_state->AddCallback(
+ std::make_unique<CompilationStateCallback>(job));
if (base::TimeTicks::IsHighResolution()) {
auto compile_mode = job->stream_ == nullptr
? CompilationTimeCallback::kAsync
: CompilationTimeCallback::kStreaming;
- compilation_state->AddCallback(CompilationTimeCallback{
+ compilation_state->AddCallback(std::make_unique<CompilationTimeCallback>(
job->isolate_->async_counters(), job->isolate_->metrics_recorder(),
- job->context_id_, job->native_module_, compile_mode});
+ job->context_id_, job->native_module_, compile_mode));
}
if (start_compilation_) {
@@ -2561,13 +2592,13 @@ class AsyncCompileJob::CompileFailed : public CompileStep {
};
namespace {
-class SampleTopTierCodeSizeCallback {
+class SampleTopTierCodeSizeCallback : public CompilationEventCallback {
public:
explicit SampleTopTierCodeSizeCallback(
std::weak_ptr<NativeModule> native_module)
: native_module_(std::move(native_module)) {}
- void operator()(CompilationEvent event) {
+ void call(CompilationEvent event) override {
if (event != CompilationEvent::kFinishedTopTierCompilation) return;
if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
GetWasmEngine()->SampleTopTierCodeSizeInAllIsolates(native_module);
@@ -2600,7 +2631,7 @@ class AsyncCompileJob::CompileFinished : public CompileStep {
// Also, set a callback to sample the code size after top-tier compilation
// finished. This callback will *not* keep the NativeModule alive.
job->native_module_->compilation_state()->AddCallback(
- SampleTopTierCodeSizeCallback{job->native_module_});
+ std::make_unique<SampleTopTierCodeSizeCallback>(job->native_module_));
}
// Then finalize and publish the generated module.
job->FinishCompile(cached_native_module_ != nullptr);
@@ -3169,6 +3200,11 @@ void CompilationStateImpl::AddCompilationUnit(CompilationUnitBuilder* builder,
void CompilationStateImpl::InitializeCompilationProgressAfterDeserialization(
base::Vector<const int> missing_functions) {
+ TRACE_EVENT1("v8.wasm", "wasm.CompilationAfterDeserialization",
+ "num_missing_functions", missing_functions.size());
+ TimedHistogramScope lazy_compile_time_scope(
+ counters()->wasm_compile_after_deserialize());
+
auto* module = native_module_->module();
auto enabled_features = native_module_->enabled_features();
const bool lazy_module = IsLazyModule(module);
@@ -3202,7 +3238,7 @@ void CompilationStateImpl::InitializeCompilationProgressAfterDeserialization(
void CompilationStateImpl::InitializeRecompilation(
TieringState new_tiering_state,
- CompilationState::callback_t recompilation_finished_callback) {
+ std::unique_ptr<CompilationEventCallback> recompilation_finished_callback) {
DCHECK(!failed());
// Hold the mutex as long as possible, to synchronize between multiple
@@ -3281,7 +3317,8 @@ void CompilationStateImpl::InitializeRecompilation(
}
}
-void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
+void CompilationStateImpl::AddCallback(
+ std::unique_ptr<CompilationEventCallback> callback) {
base::MutexGuard callbacks_guard(&callbacks_mutex_);
// Immediately trigger events that already happened.
for (auto event : {CompilationEvent::kFinishedExportWrappers,
@@ -3289,7 +3326,7 @@ void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
CompilationEvent::kFinishedTopTierCompilation,
CompilationEvent::kFailedCompilation}) {
if (finished_events_.contains(event)) {
- callback(event);
+ callback->call(event);
}
}
constexpr base::EnumSet<CompilationEvent> kFinalEvents{
@@ -3360,12 +3397,13 @@ void CompilationStateImpl::FinalizeJSToWasmWrappers(
*export_wrappers_out = isolate->factory()->NewFixedArray(
MaxNumExportWrappers(module), AllocationType::kOld);
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
- // optimization we keep the code space unlocked to avoid repeated unlocking
- // because many such wrapper are allocated in sequence below.
+ // optimization we create a code memory modification scope that avoids
+ // changing the page permissions back-and-forth between RWX and RX, because
+ // many such wrapper are allocated in sequence below.
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
"wasm.FinalizeJSToWasmWrappers", "wrappers",
js_to_wasm_wrapper_units_.size());
- CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+ CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
for (auto& unit : js_to_wasm_wrapper_units_) {
DCHECK_EQ(isolate, unit->isolate());
Handle<Code> code = unit->Finalize();
@@ -3549,19 +3587,21 @@ void CompilationStateImpl::TriggerCallbacks(
DCHECK_NE(compilation_id_, kInvalidCompilationID);
TRACE_EVENT1("v8.wasm", event.second, "id", compilation_id_);
for (auto& callback : callbacks_) {
- callback(event.first);
+ callback->call(event.first);
}
}
- // With dynamic tiering, we don't know if we can ever delete the callback.
- // TODO(https://crbug.com/v8/12289): Release some callbacks also when dynamic
- // tiering is enabled.
- if (dynamic_tiering_ == DynamicTiering::kDisabled &&
- outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0 &&
+ if (outstanding_baseline_units_ == 0 && outstanding_export_wrappers_ == 0 &&
outstanding_top_tier_functions_ == 0 &&
outstanding_recompilation_functions_ == 0) {
- // Clear the callbacks because no more events will be delivered.
- callbacks_.clear();
+ callbacks_.erase(
+ std::remove_if(
+ callbacks_.begin(), callbacks_.end(),
+ [](std::unique_ptr<CompilationEventCallback>& event) {
+ return event->release_after_final_event() ==
+ CompilationEventCallback::ReleaseAfterFinalEvent::kRelease;
+ }),
+ callbacks_.end());
}
}
@@ -3668,6 +3708,27 @@ void CompilationStateImpl::SetError() {
void CompilationStateImpl::WaitForCompilationEvent(
CompilationEvent expect_event) {
+ class WaitForCompilationEventCallback : public CompilationEventCallback {
+ public:
+ WaitForCompilationEventCallback(std::shared_ptr<base::Semaphore> semaphore,
+ std::shared_ptr<std::atomic<bool>> done,
+ base::EnumSet<CompilationEvent> events)
+ : semaphore_(std::move(semaphore)),
+ done_(std::move(done)),
+ events_(events) {}
+
+ void call(CompilationEvent event) override {
+ if (!events_.contains(event)) return;
+ done_->store(true, std::memory_order_relaxed);
+ semaphore_->Signal();
+ }
+
+ private:
+ std::shared_ptr<base::Semaphore> semaphore_;
+ std::shared_ptr<std::atomic<bool>> done_;
+ base::EnumSet<CompilationEvent> events_;
+ };
+
auto semaphore = std::make_shared<base::Semaphore>(0);
auto done = std::make_shared<std::atomic<bool>>(false);
base::EnumSet<CompilationEvent> events{expect_event,
@@ -3675,11 +3736,8 @@ void CompilationStateImpl::WaitForCompilationEvent(
{
base::MutexGuard callbacks_guard(&callbacks_mutex_);
if (finished_events_.contains_any(events)) return;
- callbacks_.emplace_back([semaphore, events, done](CompilationEvent event) {
- if (!events.contains(event)) return;
- done->store(true, std::memory_order_relaxed);
- semaphore->Signal();
- });
+ callbacks_.emplace_back(std::make_unique<WaitForCompilationEventCallback>(
+ semaphore, done, events));
}
class WaitForEventDelegate final : public JobDelegate {
@@ -3798,9 +3856,10 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
// Finalize compilation jobs in the main thread.
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
- // optimization we keep the code space unlocked to avoid repeated unlocking
- // because many such wrapper are allocated in sequence below.
- CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
+ // optimization we create a code memory modification scope that avoids
+ // changing the page permissions back-and-forth between RWX and RX, because
+ // many such wrapper are allocated in sequence below.
+ CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
for (auto& pair : compilation_units) {
JSToWasmWrapperKey key = pair.first;
JSToWasmWrapperCompilationUnit* unit = pair.second.get();