/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/baseline/baseline-batch-compiler.cc
397 строк
14 KB
Michaël Zasso
deps: update V8 to 14.1.146.11
04 окт 2025, 19:47
Не верифицирован
04 окт 2025, 19:47
7772a2d
Код
Авторство
О чём код?
// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/baseline/baseline-batch-compiler.h" #include <algorithm> #include "src/base/fpu.h" #include "src/baseline/baseline-compiler.h" #include "src/codegen/compiler.h" #include "src/execution/isolate.h" #include "src/handles/global-handles-inl.h" #include "src/heap/factory-inl.h" #include "src/heap/heap-inl.h" #include "src/heap/local-heap-inl.h" #include "src/heap/parked-scope.h" #include "src/objects/fixed-array-inl.h" #include "src/objects/js-function-inl.h" #include "src/utils/locked-queue-inl.h" namespace v8 { namespace internal { namespace baseline { static bool CanCompileWithConcurrentBaseline(Tagged<SharedFunctionInfo> shared, Isolate* isolate) { return !shared->HasBaselineCode() && CanCompileWithBaseline(isolate, shared); } class BaselineCompilerTask { public: BaselineCompilerTask(Isolate* isolate, PersistentHandles* handles, Tagged<SharedFunctionInfo> sfi) : shared_function_info_(handles->NewHandle(sfi)), bytecode_(handles->NewHandle(sfi->GetBytecodeArray(isolate))) { DCHECK(sfi->is_compiled()); shared_function_info_->set_is_sparkplug_compiling(true); } BaselineCompilerTask(const BaselineCompilerTask&) V8_NOEXCEPT = delete; BaselineCompilerTask(BaselineCompilerTask&&) V8_NOEXCEPT = default; // Executed in the background thread. void Compile(LocalIsolate* local_isolate) { RCS_SCOPE(local_isolate, RuntimeCallCounterId::kCompileBackgroundBaseline); base::ScopedTimer timer(v8_flags.log_function_events ? &time_taken_ : nullptr); BaselineCompiler compiler(local_isolate, shared_function_info_, bytecode_); compiler.GenerateCode(); maybe_code_ = local_isolate->heap()->NewPersistentMaybeHandle(compiler.Build()); } // Executed in the main thread. void Install(Isolate* isolate) { shared_function_info_->set_is_sparkplug_compiling(false); Handle<Code> code; if (!maybe_code_.ToHandle(&code)) return; if (v8_flags.print_code) { Print(*code); } // Don't install the code if the bytecode has been flushed or has // already some baseline code installed. if (!CanCompileWithConcurrentBaseline(*shared_function_info_, isolate)) { return; } shared_function_info_->set_baseline_code(*code, kReleaseStore); shared_function_info_->set_age(0); if (v8_flags.trace_baseline) { CodeTracer::Scope scope(isolate->GetCodeTracer()); std::stringstream ss; ss << "[Concurrent Sparkplug Off Thread] Function "; ShortPrint(*shared_function_info_, ss); ss << " installed\n"; OFStream os(scope.file()); os << ss.str(); } if (IsScript(shared_function_info_->script())) { Compiler::LogFunctionCompilation( isolate, LogEventListener::CodeTag::kFunction, direct_handle(Cast<Script>(shared_function_info_->script()), isolate), shared_function_info_, DirectHandle<FeedbackVector>(), Cast<AbstractCode>(code), CodeKind::BASELINE, time_taken_.InMillisecondsF()); } } private: IndirectHandle<SharedFunctionInfo> shared_function_info_; IndirectHandle<BytecodeArray> bytecode_; MaybeIndirectHandle<Code> maybe_code_; base::TimeDelta time_taken_; }; class BaselineBatchCompilerJob { public: BaselineBatchCompilerJob(Isolate* isolate, DirectHandle<WeakFixedArray> task_queue, int batch_size) { handles_ = isolate->NewPersistentHandles(); tasks_.reserve(batch_size); for (int i = 0; i < batch_size; i++) { Tagged<MaybeObject> maybe_sfi = task_queue->get(i); // TODO(victorgomes): Do I need to clear the value? task_queue->set(i, kClearedWeakValue); Tagged<HeapObject> obj; // Skip functions where weak reference is no longer valid. if (!maybe_sfi.GetHeapObjectIfWeak(&obj)) continue; // Skip functions where the bytecode has been flushed. Tagged<SharedFunctionInfo> shared = Cast<SharedFunctionInfo>(obj); if (!CanCompileWithConcurrentBaseline(shared, isolate)) continue; // Skip functions that are already being compiled. if (shared->is_sparkplug_compiling()) continue; tasks_.emplace_back(isolate, handles_.get(), shared); } if (v8_flags.trace_baseline) { CodeTracer::Scope scope(isolate->GetCodeTracer()); PrintF(scope.file(), "[Concurrent Sparkplug] compiling %zu functions\n", tasks_.size()); } } // Executed in the background thread. void Compile(LocalIsolate* local_isolate) { local_isolate->heap()->AttachPersistentHandles(std::move(handles_)); for (auto& task : tasks_) { task.Compile(local_isolate); } // Get the handle back since we'd need them to install the code later. handles_ = local_isolate->heap()->DetachPersistentHandles(); } // Executed in the main thread. void Install(Isolate* isolate) { HandleScope local_scope(isolate); for (auto& task : tasks_) { task.Install(isolate); } } private: std::vector<BaselineCompilerTask> tasks_; std::unique_ptr<PersistentHandles> handles_; }; class ConcurrentBaselineCompiler { public: class JobDispatcher : public v8::JobTask { public: JobDispatcher( Isolate* isolate, LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>>* incoming_queue, LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>>* outcoming_queue) : isolate_(isolate), incoming_queue_(incoming_queue), outgoing_queue_(outcoming_queue) {} void Run(JobDelegate* delegate) override { base::FlushDenormalsScope flush_denormals_scope( isolate_->flush_denormals()); LocalIsolate local_isolate(isolate_, ThreadKind::kBackground); UnparkedScope unparked_scope(&local_isolate); LocalHandleScope handle_scope(&local_isolate); while (!incoming_queue_->IsEmpty() && !delegate->ShouldYield()) { std::unique_ptr<BaselineBatchCompilerJob> job; if (!incoming_queue_->Dequeue(&job)) break; DCHECK_NOT_NULL(job); job->Compile(&local_isolate); outgoing_queue_->Enqueue(std::move(job)); } isolate_->stack_guard()->RequestInstallBaselineCode(); } size_t GetMaxConcurrency(size_t worker_count) const override { size_t max_threads = v8_flags.concurrent_sparkplug_max_threads; size_t num_tasks = incoming_queue_->size() + worker_count; if (max_threads > 0) { return std::min(max_threads, num_tasks); } return num_tasks; } private: Isolate* isolate_; LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>>* incoming_queue_; LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>>* outgoing_queue_; }; explicit ConcurrentBaselineCompiler(Isolate* isolate) : isolate_(isolate) { if (v8_flags.concurrent_sparkplug) { TaskPriority priority = v8_flags.concurrent_sparkplug_high_priority_threads ? TaskPriority::kUserBlocking : TaskPriority::kUserVisible; job_handle_ = V8::GetCurrentPlatform()->PostJob( priority, std::make_unique<JobDispatcher>(isolate_, &incoming_queue_, &outgoing_queue_)); } } ~ConcurrentBaselineCompiler() { if (job_handle_ && job_handle_->IsValid()) { // Wait for the job handle to complete, so that we know the queue // pointers are safe. job_handle_->Cancel(); } } void CompileBatch(Handle<WeakFixedArray> task_queue, int batch_size) { DCHECK(v8_flags.concurrent_sparkplug); RCS_SCOPE(isolate_, RuntimeCallCounterId::kCompileBaseline); incoming_queue_.Enqueue(std::make_unique<BaselineBatchCompilerJob>( isolate_, task_queue, batch_size)); job_handle_->NotifyConcurrencyIncrease(); } void InstallBatch() { while (!outgoing_queue_.IsEmpty()) { std::unique_ptr<BaselineBatchCompilerJob> job; outgoing_queue_.Dequeue(&job); job->Install(isolate_); } } private: Isolate* isolate_; std::unique_ptr<JobHandle> job_handle_ = nullptr; LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>> incoming_queue_; LockedQueue<std::unique_ptr<BaselineBatchCompilerJob>> outgoing_queue_; }; BaselineBatchCompiler::BaselineBatchCompiler(Isolate* isolate) : isolate_(isolate), compilation_queue_(Handle<WeakFixedArray>::null()), last_index_(0), estimated_instruction_size_(0), enabled_(true) { if (v8_flags.concurrent_sparkplug) { concurrent_compiler_ = std::make_unique<ConcurrentBaselineCompiler>(isolate_); } } BaselineBatchCompiler::~BaselineBatchCompiler() { if (!compilation_queue_.is_null()) { GlobalHandles::Destroy(compilation_queue_.location()); compilation_queue_ = Handle<WeakFixedArray>::null(); } } bool BaselineBatchCompiler::concurrent() const { return v8_flags.concurrent_sparkplug && !isolate_->EfficiencyModeEnabled(); } void BaselineBatchCompiler::EnqueueFunction(DirectHandle<JSFunction> function) { DirectHandle<SharedFunctionInfo> shared(function->shared(), isolate_); // Immediately compile the function if batch compilation is disabled. if (!is_enabled()) { IsCompiledScope is_compiled_scope( function->shared()->is_compiled_scope(isolate_)); Compiler::CompileBaseline(isolate_, function, Compiler::CLEAR_EXCEPTION, &is_compiled_scope); return; } if (ShouldCompileBatch(*shared)) { if (concurrent()) { CompileBatchConcurrent(*shared); } else { CompileBatch(function); } } else { Enqueue(shared); } } void BaselineBatchCompiler::EnqueueSFI(Tagged<SharedFunctionInfo> shared) { if (!v8_flags.concurrent_sparkplug || !is_enabled()) return; if (ShouldCompileBatch(shared)) { CompileBatchConcurrent(shared); } else { Enqueue(DirectHandle<SharedFunctionInfo>(shared, isolate_)); } } void BaselineBatchCompiler::Enqueue(DirectHandle<SharedFunctionInfo> shared) { EnsureQueueCapacity(); compilation_queue_->set(last_index_++, MakeWeak(*shared)); } void BaselineBatchCompiler::InstallBatch() { DCHECK(v8_flags.concurrent_sparkplug); concurrent_compiler_->InstallBatch(); } void BaselineBatchCompiler::EnsureQueueCapacity() { if (compilation_queue_.is_null()) { compilation_queue_ = isolate_->global_handles()->Create( *isolate_->factory()->NewWeakFixedArray(kInitialQueueSize, AllocationType::kOld)); return; } if (last_index_ >= compilation_queue_->length()) { DirectHandle<WeakFixedArray> new_queue = isolate_->factory()->CopyWeakFixedArrayAndGrow(compilation_queue_, last_index_); GlobalHandles::Destroy(compilation_queue_.location()); compilation_queue_ = isolate_->global_handles()->Create(*new_queue); } } void BaselineBatchCompiler::CompileBatch(DirectHandle<JSFunction> function) { { IsCompiledScope is_compiled_scope( function->shared()->is_compiled_scope(isolate_)); Compiler::CompileBaseline(isolate_, function, Compiler::CLEAR_EXCEPTION, &is_compiled_scope); } for (int i = 0; i < last_index_; i++) { Tagged<MaybeObject> maybe_sfi = compilation_queue_->get(i); MaybeCompileFunction(maybe_sfi); compilation_queue_->set(i, kClearedWeakValue); } ClearBatch(); } void BaselineBatchCompiler::CompileBatchConcurrent( Tagged<SharedFunctionInfo> shared) { Enqueue(DirectHandle<SharedFunctionInfo>(shared, isolate_)); concurrent_compiler_->CompileBatch(compilation_queue_, last_index_); ClearBatch(); } bool BaselineBatchCompiler::ShouldCompileBatch( Tagged<SharedFunctionInfo> shared) { // Early return if the function is compiled with baseline already or it is not // suitable for baseline compilation. if (shared->HasBaselineCode()) return false; // If we're already compiling this function, return. if (shared->is_sparkplug_compiling()) return false; if (!CanCompileWithBaseline(isolate_, shared)) return false; int estimated_size; { DisallowHeapAllocation no_gc; estimated_size = BaselineCompiler::EstimateInstructionSize( shared->GetBytecodeArray(isolate_)); } estimated_instruction_size_ += estimated_size; if (v8_flags.trace_baseline_batch_compilation) { CodeTracer::Scope trace_scope(isolate_->GetCodeTracer()); PrintF(trace_scope.file(), "[Baseline batch compilation] Enqueued SFI %s", shared->DebugNameCStr().get()); PrintF(trace_scope.file(), " with estimated size %d (current budget: %d/%d)\n", estimated_size, estimated_instruction_size_, v8_flags.baseline_batch_compilation_threshold.value()); } if (estimated_instruction_size_ >= v8_flags.baseline_batch_compilation_threshold) { if (v8_flags.trace_baseline_batch_compilation) { CodeTracer::Scope trace_scope(isolate_->GetCodeTracer()); PrintF(trace_scope.file(), "[Baseline batch compilation] Compiling current batch of %d " "functions\n", (last_index_ + 1)); } return true; } return false; } bool BaselineBatchCompiler::MaybeCompileFunction( Tagged<MaybeObject> maybe_sfi) { Tagged<HeapObject> heapobj; // Skip functions where the weak reference is no longer valid. if (!maybe_sfi.GetHeapObjectIfWeak(&heapobj)) return false; Handle<SharedFunctionInfo> shared = handle(Cast<SharedFunctionInfo>(heapobj), isolate_); // Skip functions where the bytecode has been flushed. if (!shared->is_compiled()) return false; IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate_)); return Compiler::CompileSharedWithBaseline( isolate_, shared, Compiler::CLEAR_EXCEPTION, &is_compiled_scope); } void BaselineBatchCompiler::ClearBatch() { estimated_instruction_size_ = 0; last_index_ = 0; } } // namespace baseline } // namespace internal } // namespace v8