1 // Copyright 2022 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/maglev/maglev-concurrent-dispatcher.h"
6
7 #include "src/codegen/compiler.h"
8 #include "src/compiler/compilation-dependencies.h"
9 #include "src/compiler/js-heap-broker.h"
10 #include "src/execution/isolate.h"
11 #include "src/flags/flags.h"
12 #include "src/handles/persistent-handles.h"
13 #include "src/maglev/maglev-compilation-info.h"
14 #include "src/maglev/maglev-compiler.h"
15 #include "src/maglev/maglev-graph-labeller.h"
16 #include "src/objects/js-function-inl.h"
17 #include "src/utils/identity-map.h"
18 #include "src/utils/locked-queue-inl.h"
19
20 namespace v8 {
21 namespace internal {
22
23 namespace compiler {
24
AttachLocalIsolateForMaglev(maglev::MaglevCompilationInfo * info,LocalIsolate * local_isolate)25 void JSHeapBroker::AttachLocalIsolateForMaglev(
26 maglev::MaglevCompilationInfo* info, LocalIsolate* local_isolate) {
27 set_canonical_handles(info->DetachCanonicalHandles());
28 DCHECK_NULL(local_isolate_);
29 local_isolate_ = local_isolate;
30 DCHECK_NOT_NULL(local_isolate_);
31 local_isolate_->heap()->AttachPersistentHandles(
32 info->DetachPersistentHandles());
33 }
34
DetachLocalIsolateForMaglev(maglev::MaglevCompilationInfo * info)35 void JSHeapBroker::DetachLocalIsolateForMaglev(
36 maglev::MaglevCompilationInfo* info) {
37 DCHECK_NULL(ph_);
38 DCHECK_NOT_NULL(local_isolate_);
39 std::unique_ptr<PersistentHandles> ph =
40 local_isolate_->heap()->DetachPersistentHandles();
41 local_isolate_ = nullptr;
42 info->set_canonical_handles(DetachCanonicalHandles());
43 info->set_persistent_handles(std::move(ph));
44 }
45
46 } // namespace compiler
47
48 namespace maglev {
49
50 namespace {
51
52 constexpr char kMaglevCompilerName[] = "Maglev";
53
54 // LocalIsolateScope encapsulates the phase where persistent handles are
55 // attached to the LocalHeap inside {local_isolate}.
56 class V8_NODISCARD LocalIsolateScope final {
57 public:
LocalIsolateScope(MaglevCompilationInfo * info,LocalIsolate * local_isolate)58 explicit LocalIsolateScope(MaglevCompilationInfo* info,
59 LocalIsolate* local_isolate)
60 : info_(info) {
61 info_->broker()->AttachLocalIsolateForMaglev(info_, local_isolate);
62 }
63
~LocalIsolateScope()64 ~LocalIsolateScope() { info_->broker()->DetachLocalIsolateForMaglev(info_); }
65
66 private:
67 MaglevCompilationInfo* const info_;
68 };
69
70 } // namespace
71
zone() const72 Zone* ExportedMaglevCompilationInfo::zone() const { return info_->zone(); }
73
set_canonical_handles(std::unique_ptr<CanonicalHandlesMap> && canonical_handles)74 void ExportedMaglevCompilationInfo::set_canonical_handles(
75 std::unique_ptr<CanonicalHandlesMap>&& canonical_handles) {
76 info_->set_canonical_handles(std::move(canonical_handles));
77 }
78
79 // static
New(Isolate * isolate,Handle<JSFunction> function)80 std::unique_ptr<MaglevCompilationJob> MaglevCompilationJob::New(
81 Isolate* isolate, Handle<JSFunction> function) {
82 auto info = maglev::MaglevCompilationInfo::New(isolate, function);
83 return std::unique_ptr<MaglevCompilationJob>(
84 new MaglevCompilationJob(std::move(info)));
85 }
86
MaglevCompilationJob(std::unique_ptr<MaglevCompilationInfo> && info)87 MaglevCompilationJob::MaglevCompilationJob(
88 std::unique_ptr<MaglevCompilationInfo>&& info)
89 : OptimizedCompilationJob(kMaglevCompilerName, State::kReadyToPrepare),
90 info_(std::move(info)) {
91 DCHECK(FLAG_maglev);
92 }
93
94 MaglevCompilationJob::~MaglevCompilationJob() = default;
95
PrepareJobImpl(Isolate * isolate)96 CompilationJob::Status MaglevCompilationJob::PrepareJobImpl(Isolate* isolate) {
97 // TODO(v8:7700): Actual return codes.
98 return CompilationJob::SUCCEEDED;
99 }
100
ExecuteJobImpl(RuntimeCallStats * stats,LocalIsolate * local_isolate)101 CompilationJob::Status MaglevCompilationJob::ExecuteJobImpl(
102 RuntimeCallStats* stats, LocalIsolate* local_isolate) {
103 LocalIsolateScope scope{info(), local_isolate};
104 maglev::MaglevCompiler::Compile(local_isolate,
105 info()->toplevel_compilation_unit());
106 // TODO(v8:7700): Actual return codes.
107 return CompilationJob::SUCCEEDED;
108 }
109
FinalizeJobImpl(Isolate * isolate)110 CompilationJob::Status MaglevCompilationJob::FinalizeJobImpl(Isolate* isolate) {
111 Handle<CodeT> codet;
112 if (!maglev::MaglevCompiler::GenerateCode(info()->toplevel_compilation_unit())
113 .ToHandle(&codet)) {
114 return CompilationJob::FAILED;
115 }
116 info()->function()->set_code(*codet);
117 return CompilationJob::SUCCEEDED;
118 }
119
function() const120 Handle<JSFunction> MaglevCompilationJob::function() const {
121 return info_->function();
122 }
123
124 // The JobTask is posted to V8::GetCurrentPlatform(). It's responsible for
125 // processing the incoming queue on a worker thread.
126 class MaglevConcurrentDispatcher::JobTask final : public v8::JobTask {
127 public:
JobTask(MaglevConcurrentDispatcher * dispatcher)128 explicit JobTask(MaglevConcurrentDispatcher* dispatcher)
129 : dispatcher_(dispatcher) {}
130
Run(JobDelegate * delegate)131 void Run(JobDelegate* delegate) override {
132 LocalIsolate local_isolate(isolate(), ThreadKind::kBackground);
133 DCHECK(local_isolate.heap()->IsParked());
134
135 while (!incoming_queue()->IsEmpty() && !delegate->ShouldYield()) {
136 std::unique_ptr<MaglevCompilationJob> job;
137 if (!incoming_queue()->Dequeue(&job)) break;
138 DCHECK_NOT_NULL(job);
139 RuntimeCallStats* rcs = nullptr; // TODO(v8:7700): Implement.
140 CompilationJob::Status status = job->ExecuteJob(rcs, &local_isolate);
141 CHECK_EQ(status, CompilationJob::SUCCEEDED);
142 outgoing_queue()->Enqueue(std::move(job));
143 }
144 isolate()->stack_guard()->RequestInstallMaglevCode();
145 }
146
GetMaxConcurrency(size_t) const147 size_t GetMaxConcurrency(size_t) const override {
148 return incoming_queue()->size();
149 }
150
151 private:
isolate() const152 Isolate* isolate() const { return dispatcher_->isolate_; }
incoming_queue() const153 QueueT* incoming_queue() const { return &dispatcher_->incoming_queue_; }
outgoing_queue() const154 QueueT* outgoing_queue() const { return &dispatcher_->outgoing_queue_; }
155
156 MaglevConcurrentDispatcher* const dispatcher_;
157 const Handle<JSFunction> function_;
158 };
159
MaglevConcurrentDispatcher(Isolate * isolate)160 MaglevConcurrentDispatcher::MaglevConcurrentDispatcher(Isolate* isolate)
161 : isolate_(isolate) {
162 if (FLAG_concurrent_recompilation && FLAG_maglev) {
163 job_handle_ = V8::GetCurrentPlatform()->PostJob(
164 TaskPriority::kUserVisible, std::make_unique<JobTask>(this));
165 DCHECK(is_enabled());
166 } else {
167 DCHECK(!is_enabled());
168 }
169 }
170
~MaglevConcurrentDispatcher()171 MaglevConcurrentDispatcher::~MaglevConcurrentDispatcher() {
172 if (is_enabled() && job_handle_->IsValid()) {
173 // Wait for the job handle to complete, so that we know the queue
174 // pointers are safe.
175 job_handle_->Cancel();
176 }
177 }
178
EnqueueJob(std::unique_ptr<MaglevCompilationJob> && job)179 void MaglevConcurrentDispatcher::EnqueueJob(
180 std::unique_ptr<MaglevCompilationJob>&& job) {
181 DCHECK(is_enabled());
182 // TODO(v8:7700): RCS.
183 // RCS_SCOPE(isolate_, RuntimeCallCounterId::kCompileMaglev);
184 incoming_queue_.Enqueue(std::move(job));
185 job_handle_->NotifyConcurrencyIncrease();
186 }
187
FinalizeFinishedJobs()188 void MaglevConcurrentDispatcher::FinalizeFinishedJobs() {
189 HandleScope handle_scope(isolate_);
190 while (!outgoing_queue_.IsEmpty()) {
191 std::unique_ptr<MaglevCompilationJob> job;
192 outgoing_queue_.Dequeue(&job);
193 CompilationJob::Status status = job->FinalizeJob(isolate_);
194 // TODO(v8:7700): Use the result and check if job succeed
195 // when all the bytecodes are implemented.
196 if (status == CompilationJob::SUCCEEDED) {
197 Compiler::FinalizeMaglevCompilationJob(job.get(), isolate_);
198 }
199 }
200 }
201
202 } // namespace maglev
203 } // namespace internal
204 } // namespace v8
205