Line data Source code
1 : // Copyright 2016 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/compiler-dispatcher/unoptimized-compile-job.h"
6 :
7 : #include "src/assert-scope.h"
8 : #include "src/base/optional.h"
9 : #include "src/compilation-info.h"
10 : #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
11 : #include "src/compiler.h"
12 : #include "src/flags.h"
13 : #include "src/global-handles.h"
14 : #include "src/isolate.h"
15 : #include "src/objects-inl.h"
16 : #include "src/parsing/parse-info.h"
17 : #include "src/parsing/parser.h"
18 : #include "src/parsing/scanner-character-streams.h"
19 : #include "src/unicode-cache.h"
20 : #include "src/utils.h"
21 :
22 : namespace v8 {
23 : namespace internal {
24 :
25 : namespace {
26 :
27 : class OneByteWrapper : public v8::String::ExternalOneByteStringResource {
28 : public:
29 25 : OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
30 50 : ~OneByteWrapper() override = default;
31 :
32 50 : const char* data() const override {
33 50 : return reinterpret_cast<const char*>(data_);
34 : }
35 :
36 25 : size_t length() const override { return static_cast<size_t>(length_); }
37 :
38 : private:
39 : const void* data_;
40 : int length_;
41 :
42 : DISALLOW_COPY_AND_ASSIGN(OneByteWrapper);
43 : };
44 :
45 : class TwoByteWrapper : public v8::String::ExternalStringResource {
46 : public:
47 0 : TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
48 0 : ~TwoByteWrapper() override = default;
49 :
50 0 : const uint16_t* data() const override {
51 0 : return reinterpret_cast<const uint16_t*>(data_);
52 : }
53 :
54 0 : size_t length() const override { return static_cast<size_t>(length_); }
55 :
56 : private:
57 : const void* data_;
58 : int length_;
59 :
60 : DISALLOW_COPY_AND_ASSIGN(TwoByteWrapper);
61 : };
62 :
63 : } // namespace
64 :
65 105 : UnoptimizedCompileJob::UnoptimizedCompileJob(Isolate* isolate,
66 : CompilerDispatcherTracer* tracer,
67 : Handle<SharedFunctionInfo> shared,
68 : size_t max_stack_size)
69 : : status_(Status::kInitial),
70 70 : main_thread_id_(isolate->thread_id().ToInteger()),
71 : tracer_(tracer),
72 : context_(isolate->global_handles()->Create(isolate->context())),
73 : shared_(isolate->global_handles()->Create(*shared)),
74 : max_stack_size_(max_stack_size),
75 210 : trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
76 : DCHECK(!shared_->is_toplevel());
77 : HandleScope scope(isolate);
78 : Handle<Script> script(Script::cast(shared_->script()), isolate);
79 : Handle<String> source(String::cast(script->source()), isolate);
80 35 : if (trace_compiler_dispatcher_jobs_) {
81 0 : PrintF("UnoptimizedCompileJob[%p] created for ", static_cast<void*>(this));
82 : ShortPrintOnMainThread();
83 0 : PrintF(" in initial state.\n");
84 : }
85 35 : }
86 :
87 70 : UnoptimizedCompileJob::~UnoptimizedCompileJob() {
88 : DCHECK(status_ == Status::kInitial ||
89 : status_ == Status::kDone);
90 35 : if (!shared_.is_null()) {
91 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
92 35 : i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location());
93 : }
94 35 : if (!context_.is_null()) {
95 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
96 35 : i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location());
97 : }
98 70 : }
99 :
100 0 : bool UnoptimizedCompileJob::IsAssociatedWith(
101 : Handle<SharedFunctionInfo> shared) const {
102 0 : return *shared_ == *shared;
103 : }
104 :
105 346 : void UnoptimizedCompileJob::StepNextOnMainThread(Isolate* isolate) {
106 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
107 :
108 : // Ensure we are in the correct context for the job.
109 173 : SaveContext save(isolate);
110 173 : if (has_context()) {
111 : isolate->set_context(context());
112 : } else {
113 : // Phases which can run off the main thread by definition can't execute any
114 : // JS code, and so we don't need to enter their context.
115 : DCHECK(CanStepNextOnAnyThread());
116 : }
117 :
118 173 : switch (status()) {
119 : case Status::kInitial:
120 29 : return PrepareToParseOnMainThread(isolate);
121 :
122 : case Status::kReadyToParse:
123 27 : return Parse();
124 :
125 : case Status::kParsed:
126 27 : return FinalizeParsingOnMainThread(isolate);
127 :
128 : case Status::kReadyToAnalyze:
129 26 : return AnalyzeOnMainThread(isolate);
130 :
131 : case Status::kAnalyzed:
132 25 : return PrepareToCompileOnMainThread(isolate);
133 :
134 : case Status::kReadyToCompile:
135 17 : return Compile();
136 :
137 : case Status::kCompiled:
138 22 : return FinalizeCompilingOnMainThread(isolate);
139 :
140 : case Status::kFailed:
141 : case Status::kDone:
142 : return;
143 : }
144 0 : UNREACHABLE();
145 : }
146 :
147 6 : void UnoptimizedCompileJob::StepNextOnBackgroundThread() {
148 : DCHECK(CanStepNextOnAnyThread());
149 6 : switch (status()) {
150 : case Status::kReadyToParse:
151 0 : return Parse();
152 :
153 : case Status::kReadyToCompile:
154 6 : return Compile();
155 :
156 : default:
157 0 : UNREACHABLE();
158 : }
159 : }
160 :
161 83 : void UnoptimizedCompileJob::PrepareToParseOnMainThread(Isolate* isolate) {
162 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
163 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
164 : DCHECK_EQ(status(), Status::kInitial);
165 116 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse);
166 29 : if (trace_compiler_dispatcher_jobs_) {
167 : PrintF("UnoptimizedCompileJob[%p]: Preparing to parse\n",
168 0 : static_cast<void*>(this));
169 : }
170 : HandleScope scope(isolate);
171 29 : unicode_cache_.reset(new UnicodeCache());
172 : Handle<Script> script(Script::cast(shared_->script()), isolate);
173 : DCHECK(script->type() != Script::TYPE_NATIVE);
174 :
175 : Handle<String> source(String::cast(script->source()), isolate);
176 29 : parse_info_.reset(new ParseInfo(isolate->allocator()));
177 29 : parse_info_->InitFromIsolate(isolate);
178 58 : if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
179 : std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
180 4 : source, shared_->start_position(), shared_->end_position()));
181 8 : parse_info_->set_character_stream(std::move(stream));
182 : } else {
183 25 : source = String::Flatten(source);
184 : const void* data;
185 : int offset = 0;
186 : int length = source->length();
187 :
188 : // Objects in lo_space don't move, so we can just read the contents from
189 : // any thread.
190 25 : if (isolate->heap()->lo_space()->Contains(*source)) {
191 : // We need to globalize the handle to the flattened string here, in
192 : // case it's not referenced from anywhere else.
193 0 : source_ = isolate->global_handles()->Create(*source);
194 : DisallowHeapAllocation no_allocation;
195 0 : String::FlatContent content = source->GetFlatContent();
196 : DCHECK(content.IsFlat());
197 : data =
198 0 : content.IsOneByte()
199 : ? reinterpret_cast<const void*>(content.ToOneByteVector().start())
200 0 : : reinterpret_cast<const void*>(content.ToUC16Vector().start());
201 : } else {
202 : // Otherwise, create a copy of the part of the string we'll parse in the
203 : // zone.
204 25 : length = (shared_->end_position() - shared_->start_position());
205 : offset = shared_->start_position();
206 :
207 25 : int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2);
208 50 : data = parse_info_->zone()->New(byte_len);
209 :
210 : DisallowHeapAllocation no_allocation;
211 25 : String::FlatContent content = source->GetFlatContent();
212 : DCHECK(content.IsFlat());
213 25 : if (content.IsOneByte()) {
214 : MemCopy(const_cast<void*>(data),
215 25 : &content.ToOneByteVector().at(shared_->start_position()),
216 : byte_len);
217 : } else {
218 : MemCopy(const_cast<void*>(data),
219 0 : &content.ToUC16Vector().at(shared_->start_position()),
220 : byte_len);
221 : }
222 : }
223 : Handle<String> wrapper;
224 25 : if (source->IsOneByteRepresentation()) {
225 : ExternalOneByteString::Resource* resource =
226 25 : new OneByteWrapper(data, length);
227 : source_wrapper_.reset(resource);
228 : wrapper = isolate->factory()
229 : ->NewExternalStringFromOneByte(resource)
230 50 : .ToHandleChecked();
231 : } else {
232 : ExternalTwoByteString::Resource* resource =
233 0 : new TwoByteWrapper(data, length);
234 : source_wrapper_.reset(resource);
235 : wrapper = isolate->factory()
236 : ->NewExternalStringFromTwoByte(resource)
237 0 : .ToHandleChecked();
238 : }
239 25 : wrapper_ = isolate->global_handles()->Create(*wrapper);
240 : std::unique_ptr<Utf16CharacterStream> stream(
241 : ScannerStream::For(wrapper_, shared_->start_position() - offset,
242 50 : shared_->end_position() - offset));
243 50 : parse_info_->set_character_stream(std::move(stream));
244 : }
245 : parse_info_->set_hash_seed(isolate->heap()->HashSeed());
246 : parse_info_->set_is_named_expression(shared_->is_named_expression());
247 : parse_info_->set_compiler_hints(shared_->compiler_hints());
248 : parse_info_->set_start_position(shared_->start_position());
249 : parse_info_->set_end_position(shared_->end_position());
250 : parse_info_->set_unicode_cache(unicode_cache_.get());
251 : parse_info_->set_language_mode(shared_->language_mode());
252 : parse_info_->set_function_literal_id(shared_->function_literal_id());
253 29 : if (V8_UNLIKELY(FLAG_runtime_stats)) {
254 : parse_info_->set_runtime_call_stats(new (parse_info_->zone())
255 0 : RuntimeCallStats());
256 : }
257 :
258 29 : parser_.reset(new Parser(parse_info_.get()));
259 : MaybeHandle<ScopeInfo> outer_scope_info;
260 56 : if (!shared_->outer_scope_info()->IsTheHole(isolate) &&
261 : ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
262 16 : outer_scope_info = handle(ScopeInfo::cast(shared_->outer_scope_info()));
263 : }
264 29 : parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info);
265 :
266 : Handle<String> name(shared_->name());
267 : parse_info_->set_function_name(
268 29 : parse_info_->ast_value_factory()->GetString(name));
269 58 : status_ = Status::kReadyToParse;
270 29 : }
271 :
272 27 : void UnoptimizedCompileJob::Parse() {
273 : DCHECK_EQ(status(), Status::kReadyToParse);
274 108 : COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
275 : tracer_, kParse,
276 : parse_info_->end_position() - parse_info_->start_position());
277 27 : if (trace_compiler_dispatcher_jobs_) {
278 0 : PrintF("UnoptimizedCompileJob[%p]: Parsing\n", static_cast<void*>(this));
279 : }
280 :
281 : DisallowHeapAllocation no_allocation;
282 : DisallowHandleAllocation no_handles;
283 : DisallowHandleDereference no_deref;
284 :
285 27 : uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
286 :
287 : parser_->set_stack_limit(stack_limit);
288 27 : parser_->ParseOnBackground(parse_info_.get());
289 54 : status_ = Status::kParsed;
290 27 : }
291 :
292 27 : void UnoptimizedCompileJob::FinalizeParsingOnMainThread(Isolate* isolate) {
293 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
294 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
295 : DCHECK_EQ(status(), Status::kParsed);
296 108 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing);
297 27 : if (trace_compiler_dispatcher_jobs_) {
298 : PrintF("UnoptimizedCompileJob[%p]: Finalizing parsing\n",
299 0 : static_cast<void*>(this));
300 : }
301 :
302 27 : if (!source_.is_null()) {
303 0 : i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
304 0 : source_ = Handle<String>::null();
305 : }
306 27 : if (!wrapper_.is_null()) {
307 23 : i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
308 23 : wrapper_ = Handle<String>::null();
309 : }
310 :
311 : Handle<Script> script(Script::cast(shared_->script()), isolate);
312 : parse_info_->set_script(script);
313 :
314 52 : if (!shared_->outer_scope_info()->IsTheHole(isolate) &&
315 : ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
316 : Handle<ScopeInfo> outer_scope_info(
317 : handle(ScopeInfo::cast(shared_->outer_scope_info())));
318 : parse_info_->set_outer_scope_info(outer_scope_info);
319 : }
320 :
321 27 : if (parse_info_->literal() == nullptr) {
322 1 : parser_->ReportErrors(isolate, script);
323 1 : status_ = Status::kFailed;
324 : } else {
325 : parse_info_->literal()->scope()->AttachOuterScopeInfo(parse_info_.get(),
326 26 : isolate);
327 26 : status_ = Status::kReadyToAnalyze;
328 : }
329 27 : parser_->UpdateStatistics(isolate, script);
330 27 : parse_info_->UpdateStatisticsAfterBackgroundParse(isolate);
331 :
332 27 : parser_->HandleSourceURLComments(isolate, script);
333 :
334 : parse_info_->set_unicode_cache(nullptr);
335 : parser_.reset();
336 27 : unicode_cache_.reset();
337 27 : }
338 :
339 26 : void UnoptimizedCompileJob::AnalyzeOnMainThread(Isolate* isolate) {
340 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
341 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
342 : DCHECK_EQ(status(), Status::kReadyToAnalyze);
343 104 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kAnalyze);
344 26 : if (trace_compiler_dispatcher_jobs_) {
345 0 : PrintF("UnoptimizedCompileJob[%p]: Analyzing\n", static_cast<void*>(this));
346 : }
347 :
348 26 : if (Compiler::Analyze(parse_info_.get())) {
349 25 : status_ = Status::kAnalyzed;
350 : } else {
351 1 : status_ = Status::kFailed;
352 1 : if (!isolate->has_pending_exception()) isolate->StackOverflow();
353 26 : }
354 26 : }
355 :
356 25 : void UnoptimizedCompileJob::PrepareToCompileOnMainThread(Isolate* isolate) {
357 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
358 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
359 : DCHECK_EQ(status(), Status::kAnalyzed);
360 100 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile);
361 :
362 : compilation_job_.reset(
363 25 : Compiler::PrepareUnoptimizedCompilationJob(parse_info_.get(), isolate));
364 25 : if (!compilation_job_.get()) {
365 0 : if (!isolate->has_pending_exception()) isolate->StackOverflow();
366 0 : status_ = Status::kFailed;
367 25 : return;
368 : }
369 :
370 25 : CHECK(compilation_job_->can_execute_on_background_thread());
371 50 : status_ = Status::kReadyToCompile;
372 : }
373 :
374 23 : void UnoptimizedCompileJob::Compile() {
375 : DCHECK_EQ(status(), Status::kReadyToCompile);
376 92 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kCompile);
377 23 : if (trace_compiler_dispatcher_jobs_) {
378 0 : PrintF("UnoptimizedCompileJob[%p]: Compiling\n", static_cast<void*>(this));
379 : }
380 :
381 : // Disallowing of handle dereference and heap access dealt with in
382 : // CompilationJob::ExecuteJob.
383 :
384 23 : uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
385 : compilation_job_->set_stack_limit(stack_limit);
386 :
387 23 : CompilationJob::Status status = compilation_job_->ExecuteJob();
388 : USE(status);
389 :
390 : // Always transition to kCompiled - errors will be reported by
391 : // FinalizeCompilingOnMainThread.
392 46 : status_ = Status::kCompiled;
393 23 : }
394 :
395 22 : void UnoptimizedCompileJob::FinalizeCompilingOnMainThread(Isolate* isolate) {
396 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
397 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
398 : DCHECK_EQ(status(), Status::kCompiled);
399 85 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling);
400 22 : if (trace_compiler_dispatcher_jobs_) {
401 : PrintF("UnoptimizedCompileJob[%p]: Finalizing compiling\n",
402 0 : static_cast<void*>(this));
403 : }
404 :
405 : {
406 : HandleScope scope(isolate);
407 : // Internalize ast values onto the heap.
408 22 : parse_info_->ast_value_factory()->Internalize(isolate);
409 : // Allocate scope infos for the literal.
410 : DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate,
411 22 : AnalyzeMode::kRegular);
412 22 : compilation_job_->compilation_info()->set_shared_info(shared_);
413 41 : if (compilation_job_->state() == CompilationJob::State::kFailed ||
414 19 : !Compiler::FinalizeCompilationJob(compilation_job_.release())) {
415 3 : if (!isolate->has_pending_exception()) isolate->StackOverflow();
416 3 : status_ = Status::kFailed;
417 22 : return;
418 : }
419 : }
420 :
421 : compilation_job_.reset();
422 : parse_info_.reset();
423 :
424 38 : status_ = Status::kDone;
425 : }
426 :
427 34 : void UnoptimizedCompileJob::ResetOnMainThread(Isolate* isolate) {
428 34 : if (trace_compiler_dispatcher_jobs_) {
429 0 : PrintF("UnoptimizedCompileJob[%p]: Resetting\n", static_cast<void*>(this));
430 : }
431 :
432 : compilation_job_.reset();
433 : parser_.reset();
434 : unicode_cache_.reset();
435 : parse_info_.reset();
436 :
437 34 : if (!source_.is_null()) {
438 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
439 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
440 0 : i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
441 0 : source_ = Handle<String>::null();
442 : }
443 34 : if (!wrapper_.is_null()) {
444 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
445 : DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
446 2 : i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
447 2 : wrapper_ = Handle<String>::null();
448 : }
449 :
450 34 : status_ = Status::kInitial;
451 34 : }
452 :
453 88 : double UnoptimizedCompileJob::EstimateRuntimeOfNextStepInMs() const {
454 88 : switch (status()) {
455 : case Status::kInitial:
456 12 : return tracer_->EstimatePrepareToParseInMs();
457 :
458 : case Status::kReadyToParse:
459 24 : return tracer_->EstimateParseInMs(parse_info_->end_position() -
460 24 : parse_info_->start_position());
461 :
462 : case Status::kParsed:
463 12 : return tracer_->EstimateFinalizeParsingInMs();
464 :
465 : case Status::kReadyToAnalyze:
466 12 : return tracer_->EstimateAnalyzeInMs();
467 :
468 : case Status::kAnalyzed:
469 12 : return tracer_->EstimatePrepareToCompileInMs();
470 :
471 : case Status::kReadyToCompile:
472 12 : return tracer_->EstimateCompileInMs();
473 :
474 : case Status::kCompiled:
475 8 : return tracer_->EstimateFinalizeCompilingInMs();
476 :
477 : case Status::kFailed:
478 : case Status::kDone:
479 : return 0.0;
480 : }
481 :
482 0 : UNREACHABLE();
483 : }
484 :
485 0 : void UnoptimizedCompileJob::ShortPrintOnMainThread() {
486 : DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
487 : DCHECK(!shared_.is_null());
488 0 : shared_->ShortPrint();
489 0 : }
490 :
491 : } // namespace internal
492 : } // namespace v8
|