blob: 959324a05fd84bf74e6931c7d836d5f03232ce8f [file] [log] [blame]
// Copyright 2012 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.
#ifndef V8_COMPILER_DISPATCHER_OPTIMIZING_COMPILE_DISPATCHER_H_
#define V8_COMPILER_DISPATCHER_OPTIMIZING_COMPILE_DISPATCHER_H_
#include <atomic>
#include <queue>
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/base/vector.h"
#include "src/common/globals.h"
#include "src/flags/flags.h"
#include "src/utils/allocation.h"
namespace v8 {
namespace internal {
class LocalHeap;
class TurbofanCompilationJob;
class RuntimeCallStats;
class SharedFunctionInfo;
// Align the structure to the cache line size to prevent false sharing. While a
// task state is owned by a single thread, all tasks states are kept in a vector
// in OptimizingCompileDispatcher::task_states.
struct alignas(PROCESSOR_CACHE_LINE_SIZE) OptimizingCompileTaskState {
Isolate* isolate;
};
// Circular queue of incoming recompilation tasks (including OSR).
class V8_EXPORT OptimizingCompileInputQueue {
public:
inline bool IsAvailable() {
base::MutexGuard access(&mutex_);
return queue_.size() < capacity_;
}
inline size_t Length() {
base::MutexGuard access_queue(&mutex_);
return queue_.size();
}
explicit OptimizingCompileInputQueue(int capacity) : capacity_(capacity) {}
TurbofanCompilationJob* Dequeue(OptimizingCompileTaskState& task_state);
TurbofanCompilationJob* DequeueIfIsolateMatches(Isolate* isolate);
bool Enqueue(std::unique_ptr<TurbofanCompilationJob>& job);
void FlushJobsForIsolate(Isolate* isolate);
bool HasJobForIsolate(Isolate* isolate);
void Prioritize(Isolate* isolate, Tagged<SharedFunctionInfo> function);
private:
std::deque<TurbofanCompilationJob*> queue_;
size_t capacity_;
base::Mutex mutex_;
base::ConditionVariable task_finished_;
friend class OptimizingCompileTaskExecutor;
};
// This class runs compile tasks in a thread pool. Threads grab new work from
// the input_queue_ defined in this class. Once a task is done, it will be
// enqueued into an isolate-local output queue. This class is
// not specific to a particular isolate and may be shared across multiple
// isolates in the future.
class V8_EXPORT OptimizingCompileTaskExecutor {
public:
OptimizingCompileTaskExecutor();
~OptimizingCompileTaskExecutor();
// Creates Job with PostJob.
void EnsureInitialized();
// Invokes and runs Turbofan for this particular job.
void CompileNext(Isolate* isolate, LocalIsolate& local_isolate,
TurbofanCompilationJob* job);
// Gets the next job from the input queue.
TurbofanCompilationJob* NextInput(OptimizingCompileTaskState& task_state);
// Gets the next job from the input queue but only if the job is also for the
// given isolate.
TurbofanCompilationJob* NextInputIfIsolateMatches(Isolate* isolate);
// Returns true when one of the currently running compilation tasks is
// operating on the given isolate. If the return value is false, the caller
// can also assume that no LocalHeap/LocalIsolate exists for the isolate
// anymore as well.
bool IsTaskRunningForIsolate(Isolate* isolate);
// Clears the state for a task/thread once it is done with a job. This mainly
// clears the current isolate for this task. Only invoke this after the
// LocalHeap/LocalIsolate for this thread was destroyed as well.
void ClearTaskState(OptimizingCompileTaskState& task_state);
// Tries to append a new compilation job to the input queue. This may fail if
// the input queue was already full.
bool TryQueueForOptimization(std::unique_ptr<TurbofanCompilationJob>& job);
// Waits until all running and queued compilation jobs for this isolate are
// done.
void WaitUntilCompilationJobsDoneForIsolate(Isolate* isolate);
// Returns true if there exists a currently running or queued compilation job
// for this isolate..
bool HasCompilationJobsForIsolate(Isolate* isolate);
private:
class CompileTask;
static constexpr TaskPriority kTaskPriority = TaskPriority::kUserVisible;
static constexpr TaskPriority kEfficiencyTaskPriority =
TaskPriority::kBestEffort;
OptimizingCompileInputQueue input_queue_;
// Copy of v8_flags.concurrent_recompilation_delay that will be used from the
// background thread.
//
// Since flags might get modified while the background thread is running, it
// is not safe to access them directly.
int recompilation_delay_;
std::unique_ptr<JobHandle> job_handle_;
base::OwnedVector<OptimizingCompileTaskState> task_states_;
// Used to avoid creating the JobHandle twice.
bool is_initialized_ = false;
friend class OptimizingCompileDispatcher;
};
class OptimizingCompileOutputQueue {
public:
void Enqueue(TurbofanCompilationJob* job);
std::unique_ptr<TurbofanCompilationJob> Dequeue();
int InstallGeneratedBuiltins(Isolate* isolate, int installed_count);
size_t size() const;
bool empty() const;
private:
// Queue of recompilation tasks ready to be installed (excluding OSR).
std::deque<TurbofanCompilationJob*> queue_;
// Used for job based recompilation which has multiple producers on
// different threads.
base::Mutex mutex_;
};
// OptimizingCompileDispatcher is an isolate-specific class to enqueue Turbofan
// compilation jobs and retrieve results.
class V8_EXPORT_PRIVATE OptimizingCompileDispatcher {
public:
explicit OptimizingCompileDispatcher(
Isolate* isolate, OptimizingCompileTaskExecutor* task_executor);
~OptimizingCompileDispatcher();
// Flushes input and output queue for compilation jobs. If blocking behavior
// is used, it will also wait until the running compilation jobs are done
// before flushing the output queue.
void Flush(BlockingBehavior blocking_behavior);
// Tries to append the compilation job to the input queue. Takes ownership of
// |job| if successful. Fails if the input queue is already full.
bool TryQueueForOptimization(std::unique_ptr<TurbofanCompilationJob>& job);
// Waits until all running and queued compilation jobs have finished.
void WaitUntilCompilationJobsDone();
void InstallOptimizedFunctions();
// Install generated builtins in the output queue in contiguous finalization
// order, starting with installed_count. Returns the finalization order of the
// last job that was finalized.
int InstallGeneratedBuiltins(int installed_count);
// Returns true if there is space available in the input queue.
inline bool IsQueueAvailable() { return input_queue().IsAvailable(); }
static bool Enabled() { return v8_flags.concurrent_recompilation; }
// This method must be called on the main thread.
bool HasJobs();
// Whether to finalize and thus install the optimized code. Defaults to true.
// Only set to false for testing (where finalization is then manually
// requested using %FinalizeOptimization) and when compiling embedded builtins
// concurrently. For the latter, builtins are installed manually using
// InstallGeneratedBuiltins().
bool finalize() const { return finalize_; }
void set_finalize(bool finalize) {
CHECK(!HasJobs());
finalize_ = finalize;
}
void Prioritize(Tagged<SharedFunctionInfo> function);
void StartTearDown();
void FinishTearDown();
void QueueFinishedJob(TurbofanCompilationJob* job);
private:
enum ModeFlag { COMPILE, FLUSH };
void FlushQueues(BlockingBehavior blocking_behavior);
void FlushInputQueue();
void FlushOutputQueue();
OptimizingCompileInputQueue& input_queue() {
return task_executor_->input_queue_;
}
int recompilation_delay() const {
return task_executor_->recompilation_delay_;
}
Isolate* isolate_;
OptimizingCompileTaskExecutor* task_executor_;
OptimizingCompileOutputQueue output_queue_;
bool finalize_ = true;
};
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DISPATCHER_OPTIMIZING_COMPILE_DISPATCHER_H_