1 // Copyright 2012 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/codegen/compiler.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "src/api/api-inl.h"
11 #include "src/asmjs/asm-js.h"
12 #include "src/ast/prettyprinter.h"
13 #include "src/ast/scopes.h"
14 #include "src/base/logging.h"
15 #include "src/base/optional.h"
16 #include "src/codegen/assembler-inl.h"
17 #include "src/codegen/compilation-cache.h"
18 #include "src/codegen/optimized-compilation-info.h"
19 #include "src/codegen/pending-optimization-table.h"
20 #include "src/codegen/unoptimized-compilation-info.h"
21 #include "src/common/assert-scope.h"
22 #include "src/common/globals.h"
23 #include "src/common/message-template.h"
24 #include "src/compiler-dispatcher/compiler-dispatcher.h"
25 #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
26 #include "src/compiler/pipeline.h"
27 #include "src/debug/debug.h"
28 #include "src/debug/liveedit.h"
29 #include "src/diagnostics/code-tracer.h"
30 #include "src/execution/frames-inl.h"
31 #include "src/execution/isolate-inl.h"
32 #include "src/execution/isolate.h"
33 #include "src/execution/runtime-profiler.h"
34 #include "src/execution/vm-state-inl.h"
35 #include "src/handles/maybe-handles.h"
36 #include "src/heap/heap-inl.h"
37 #include "src/heap/local-factory-inl.h"
38 #include "src/heap/local-heap-inl.h"
39 #include "src/heap/local-heap.h"
40 #include "src/init/bootstrapper.h"
41 #include "src/interpreter/interpreter.h"
42 #include "src/logging/log-inl.h"
43 #include "src/objects/feedback-cell-inl.h"
44 #include "src/objects/js-function-inl.h"
45 #include "src/objects/map.h"
46 #include "src/objects/object-list-macros.h"
47 #include "src/objects/shared-function-info.h"
48 #include "src/objects/string.h"
49 #include "src/parsing/parse-info.h"
50 #include "src/parsing/parser.h"
51 #include "src/parsing/parsing.h"
52 #include "src/parsing/pending-compilation-error-handler.h"
53 #include "src/parsing/scanner-character-streams.h"
54 #include "src/snapshot/code-serializer.h"
55 #include "src/utils/ostreams.h"
56 #include "src/zone/zone-list-inl.h" // crbug.com/v8/8816
57
58 namespace v8 {
59 namespace internal {
60
61 namespace {
62
IsForNativeContextIndependentCachingOnly(CodeKind kind)63 bool IsForNativeContextIndependentCachingOnly(CodeKind kind) {
64 // NCI code is only cached (and not installed on the JSFunction upon
65 // successful compilation), unless the testing-only
66 // FLAG_turbo_nci_as_midtier is enabled.
67 return CodeKindIsNativeContextIndependentJSFunction(kind) &&
68 !FLAG_turbo_nci_as_midtier;
69 }
70
71 // This predicate is currently needed only because the nci-as-midtier testing
72 // configuration is special. A quick summary of compilation configurations:
73 //
74 // - Turbofan (and currently Turboprop) uses both the optimization marker and
75 // the optimized code cache (underneath, the marker and the cache share the same
76 // slot on the feedback vector).
77 // - Native context independent (NCI) code uses neither the marker nor the
78 // cache.
79 // - The NCI-as-midtier testing configuration uses the marker, but not the
80 // cache.
81 //
82 // This predicate supports that last case. In the near future, this last case is
83 // expected to change s.t. code kinds use the marker iff they use the optimized
84 // code cache (details still TBD). In that case, the existing
85 // CodeKindIsStoredInOptimizedCodeCache is sufficient and this extra predicate
86 // can be removed.
87 // TODO(jgruber,rmcilroy,v8:8888): Remove this predicate once that has happened.
UsesOptimizationMarker(CodeKind kind)88 bool UsesOptimizationMarker(CodeKind kind) {
89 return !IsForNativeContextIndependentCachingOnly(kind);
90 }
91
92 class CompilerTracer : public AllStatic {
93 public:
PrintTracePrefix(const CodeTracer::Scope & scope,const char * header,OptimizedCompilationInfo * info)94 static void PrintTracePrefix(const CodeTracer::Scope& scope,
95 const char* header,
96 OptimizedCompilationInfo* info) {
97 PrintTracePrefix(scope, header, info->closure(), info->code_kind());
98 }
99
PrintTracePrefix(const CodeTracer::Scope & scope,const char * header,Handle<JSFunction> function,CodeKind code_kind)100 static void PrintTracePrefix(const CodeTracer::Scope& scope,
101 const char* header, Handle<JSFunction> function,
102 CodeKind code_kind) {
103 PrintF(scope.file(), "[%s ", header);
104 function->ShortPrint(scope.file());
105 PrintF(scope.file(), " (target %s)", CodeKindToString(code_kind));
106 }
107
PrintTraceSuffix(const CodeTracer::Scope & scope)108 static void PrintTraceSuffix(const CodeTracer::Scope& scope) {
109 PrintF(scope.file(), "]\n");
110 }
111
TracePrepareJob(Isolate * isolate,OptimizedCompilationInfo * info,const char * compiler_name)112 static void TracePrepareJob(Isolate* isolate, OptimizedCompilationInfo* info,
113 const char* compiler_name) {
114 if (!FLAG_trace_opt || !info->IsOptimizing()) return;
115 CodeTracer::Scope scope(isolate->GetCodeTracer());
116 PrintTracePrefix(scope, "compiling method", info);
117 PrintF(scope.file(), " using %s%s", compiler_name,
118 info->is_osr() ? " OSR" : "");
119 PrintTraceSuffix(scope);
120 }
121
TraceCompilationStats(Isolate * isolate,OptimizedCompilationInfo * info,double ms_creategraph,double ms_optimize,double ms_codegen)122 static void TraceCompilationStats(Isolate* isolate,
123 OptimizedCompilationInfo* info,
124 double ms_creategraph, double ms_optimize,
125 double ms_codegen) {
126 if (!FLAG_trace_opt || !info->IsOptimizing()) return;
127 CodeTracer::Scope scope(isolate->GetCodeTracer());
128 PrintTracePrefix(scope, "optimizing", info);
129 PrintF(scope.file(), " - took %0.3f, %0.3f, %0.3f ms", ms_creategraph,
130 ms_optimize, ms_codegen);
131 PrintTraceSuffix(scope);
132 }
133
TraceCompletedJob(Isolate * isolate,OptimizedCompilationInfo * info)134 static void TraceCompletedJob(Isolate* isolate,
135 OptimizedCompilationInfo* info) {
136 if (!FLAG_trace_opt) return;
137 CodeTracer::Scope scope(isolate->GetCodeTracer());
138 PrintTracePrefix(scope, "completed optimizing", info);
139 PrintTraceSuffix(scope);
140 }
141
TraceAbortedJob(Isolate * isolate,OptimizedCompilationInfo * info)142 static void TraceAbortedJob(Isolate* isolate,
143 OptimizedCompilationInfo* info) {
144 if (!FLAG_trace_opt) return;
145 CodeTracer::Scope scope(isolate->GetCodeTracer());
146 PrintTracePrefix(scope, "aborted optimizing", info);
147 PrintF(scope.file(), " because: %s",
148 GetBailoutReason(info->bailout_reason()));
149 PrintTraceSuffix(scope);
150 }
151
TraceOptimizedCodeCacheHit(Isolate * isolate,Handle<JSFunction> function,BailoutId osr_offset,CodeKind code_kind)152 static void TraceOptimizedCodeCacheHit(Isolate* isolate,
153 Handle<JSFunction> function,
154 BailoutId osr_offset,
155 CodeKind code_kind) {
156 if (!FLAG_trace_opt) return;
157 CodeTracer::Scope scope(isolate->GetCodeTracer());
158 PrintTracePrefix(scope, "found optimized code for", function, code_kind);
159 if (!osr_offset.IsNone()) {
160 PrintF(scope.file(), " at OSR AST id %d", osr_offset.ToInt());
161 }
162 PrintTraceSuffix(scope);
163 }
164
TraceOptimizeForAlwaysOpt(Isolate * isolate,Handle<JSFunction> function,CodeKind code_kind)165 static void TraceOptimizeForAlwaysOpt(Isolate* isolate,
166 Handle<JSFunction> function,
167 CodeKind code_kind) {
168 if (!FLAG_trace_opt) return;
169 CodeTracer::Scope scope(isolate->GetCodeTracer());
170 PrintTracePrefix(scope, "optimizing", function, code_kind);
171 PrintF(scope.file(), " because --always-opt");
172 PrintTraceSuffix(scope);
173 }
174
TraceMarkForAlwaysOpt(Isolate * isolate,Handle<JSFunction> function)175 static void TraceMarkForAlwaysOpt(Isolate* isolate,
176 Handle<JSFunction> function) {
177 if (!FLAG_trace_opt) return;
178 CodeTracer::Scope scope(isolate->GetCodeTracer());
179 PrintF(scope.file(), "[marking ");
180 function->ShortPrint(scope.file());
181 PrintF(scope.file(), " for optimized recompilation because --always-opt");
182 PrintF(scope.file(), "]\n");
183 }
184 };
185
186 } // namespace
187
188 // Helper that times a scoped region and records the elapsed time.
189 struct ScopedTimer {
ScopedTimerv8::internal::ScopedTimer190 explicit ScopedTimer(base::TimeDelta* location) : location_(location) {
191 DCHECK_NOT_NULL(location_);
192 timer_.Start();
193 }
194
~ScopedTimerv8::internal::ScopedTimer195 ~ScopedTimer() { *location_ += timer_.Elapsed(); }
196
197 base::ElapsedTimer timer_;
198 base::TimeDelta* location_;
199 };
200
201 namespace {
202
LogFunctionCompilation(CodeEventListener::LogEventsAndTags tag,Handle<SharedFunctionInfo> shared,Handle<Script> script,Handle<AbstractCode> abstract_code,bool optimizing,double time_taken_ms,Isolate * isolate)203 void LogFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
204 Handle<SharedFunctionInfo> shared,
205 Handle<Script> script,
206 Handle<AbstractCode> abstract_code, bool optimizing,
207 double time_taken_ms, Isolate* isolate) {
208 DCHECK(!abstract_code.is_null());
209 DCHECK(!abstract_code.is_identical_to(BUILTIN_CODE(isolate, CompileLazy)));
210
211 // Log the code generation. If source information is available include
212 // script name and line number. Check explicitly whether logging is
213 // enabled as finding the line number is not free.
214 if (!isolate->logger()->is_listening_to_code_events() &&
215 !isolate->is_profiling() && !FLAG_log_function_events &&
216 !isolate->code_event_dispatcher()->IsListeningToCodeEvents()) {
217 return;
218 }
219
220 int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1;
221 int column_num = Script::GetColumnNumber(script, shared->StartPosition()) + 1;
222 Handle<String> script_name(script->name().IsString()
223 ? String::cast(script->name())
224 : ReadOnlyRoots(isolate).empty_string(),
225 isolate);
226 CodeEventListener::LogEventsAndTags log_tag =
227 Logger::ToNativeByScript(tag, *script);
228 PROFILE(isolate, CodeCreateEvent(log_tag, abstract_code, shared, script_name,
229 line_num, column_num));
230 if (!FLAG_log_function_events) return;
231
232 DisallowHeapAllocation no_gc;
233
234 std::string name = optimizing ? "optimize" : "compile";
235 switch (tag) {
236 case CodeEventListener::EVAL_TAG:
237 name += "-eval";
238 break;
239 case CodeEventListener::SCRIPT_TAG:
240 break;
241 case CodeEventListener::LAZY_COMPILE_TAG:
242 name += "-lazy";
243 break;
244 case CodeEventListener::FUNCTION_TAG:
245 break;
246 default:
247 UNREACHABLE();
248 }
249
250 LOG(isolate, FunctionEvent(name.c_str(), script->id(), time_taken_ms,
251 shared->StartPosition(), shared->EndPosition(),
252 shared->DebugName()));
253 }
254
OriginOptionsForEval(Object script)255 ScriptOriginOptions OriginOptionsForEval(Object script) {
256 if (!script.IsScript()) return ScriptOriginOptions();
257
258 const auto outer_origin_options = Script::cast(script).origin_options();
259 return ScriptOriginOptions(outer_origin_options.IsSharedCrossOrigin(),
260 outer_origin_options.IsOpaque());
261 }
262
263 } // namespace
264
265 // ----------------------------------------------------------------------------
266 // Implementation of UnoptimizedCompilationJob
267
ExecuteJob()268 CompilationJob::Status UnoptimizedCompilationJob::ExecuteJob() {
269 DisallowHeapAccess no_heap_access;
270 // Delegate to the underlying implementation.
271 DCHECK_EQ(state(), State::kReadyToExecute);
272 ScopedTimer t(&time_taken_to_execute_);
273 return UpdateState(ExecuteJobImpl(), State::kReadyToFinalize);
274 }
275
FinalizeJob(Handle<SharedFunctionInfo> shared_info,Isolate * isolate)276 CompilationJob::Status UnoptimizedCompilationJob::FinalizeJob(
277 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
278 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
279 DisallowCodeDependencyChange no_dependency_change;
280 DisallowJavascriptExecution no_js(isolate);
281
282 // Delegate to the underlying implementation.
283 DCHECK_EQ(state(), State::kReadyToFinalize);
284 ScopedTimer t(&time_taken_to_finalize_);
285 return UpdateState(FinalizeJobImpl(shared_info, isolate), State::kSucceeded);
286 }
287
FinalizeJob(Handle<SharedFunctionInfo> shared_info,LocalIsolate * isolate)288 CompilationJob::Status UnoptimizedCompilationJob::FinalizeJob(
289 Handle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
290 // Delegate to the underlying implementation.
291 DCHECK_EQ(state(), State::kReadyToFinalize);
292 ScopedTimer t(&time_taken_to_finalize_);
293 return UpdateState(FinalizeJobImpl(shared_info, isolate), State::kSucceeded);
294 }
295
296 namespace {
297
RecordUnoptimizedCompilationStats(Isolate * isolate,Handle<SharedFunctionInfo> shared_info)298 void RecordUnoptimizedCompilationStats(Isolate* isolate,
299 Handle<SharedFunctionInfo> shared_info) {
300 int code_size;
301 if (shared_info->HasBytecodeArray()) {
302 code_size = shared_info->GetBytecodeArray().SizeIncludingMetadata();
303 } else {
304 code_size = shared_info->asm_wasm_data().Size();
305 }
306
307 Counters* counters = isolate->counters();
308 // TODO(4280): Rename counters from "baseline" to "unoptimized" eventually.
309 counters->total_baseline_code_size()->Increment(code_size);
310 counters->total_baseline_compile_count()->Increment(1);
311
312 // TODO(5203): Add timers for each phase of compilation.
313 // Also add total time (there's now already timer_ on the base class).
314 }
315
RecordUnoptimizedFunctionCompilation(Isolate * isolate,CodeEventListener::LogEventsAndTags tag,Handle<SharedFunctionInfo> shared,base::TimeDelta time_taken_to_execute,base::TimeDelta time_taken_to_finalize)316 void RecordUnoptimizedFunctionCompilation(
317 Isolate* isolate, CodeEventListener::LogEventsAndTags tag,
318 Handle<SharedFunctionInfo> shared, base::TimeDelta time_taken_to_execute,
319 base::TimeDelta time_taken_to_finalize) {
320 Handle<AbstractCode> abstract_code;
321 if (shared->HasBytecodeArray()) {
322 abstract_code =
323 handle(AbstractCode::cast(shared->GetBytecodeArray()), isolate);
324 } else {
325 DCHECK(shared->HasAsmWasmData());
326 abstract_code =
327 Handle<AbstractCode>::cast(BUILTIN_CODE(isolate, InstantiateAsmJs));
328 }
329
330 double time_taken_ms = time_taken_to_execute.InMillisecondsF() +
331 time_taken_to_finalize.InMillisecondsF();
332
333 Handle<Script> script(Script::cast(shared->script()), isolate);
334 LogFunctionCompilation(tag, shared, script, abstract_code, false,
335 time_taken_ms, isolate);
336 }
337
338 } // namespace
339
340 // ----------------------------------------------------------------------------
341 // Implementation of OptimizedCompilationJob
342
PrepareJob(Isolate * isolate)343 CompilationJob::Status OptimizedCompilationJob::PrepareJob(Isolate* isolate) {
344 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
345 DisallowJavascriptExecution no_js(isolate);
346 CompilerTracer::TracePrepareJob(isolate, compilation_info(), compiler_name_);
347
348 // Delegate to the underlying implementation.
349 DCHECK_EQ(state(), State::kReadyToPrepare);
350 ScopedTimer t(&time_taken_to_prepare_);
351 return UpdateState(PrepareJobImpl(isolate), State::kReadyToExecute);
352 }
353
ExecuteJob(RuntimeCallStats * stats,LocalIsolate * local_isolate)354 CompilationJob::Status OptimizedCompilationJob::ExecuteJob(
355 RuntimeCallStats* stats, LocalIsolate* local_isolate) {
356 DisallowHeapAccess no_heap_access;
357 // Delegate to the underlying implementation.
358 DCHECK_EQ(state(), State::kReadyToExecute);
359 ScopedTimer t(&time_taken_to_execute_);
360 return UpdateState(ExecuteJobImpl(stats, local_isolate),
361 State::kReadyToFinalize);
362 }
363
FinalizeJob(Isolate * isolate)364 CompilationJob::Status OptimizedCompilationJob::FinalizeJob(Isolate* isolate) {
365 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
366 DisallowJavascriptExecution no_js(isolate);
367
368 // Delegate to the underlying implementation.
369 DCHECK_EQ(state(), State::kReadyToFinalize);
370 ScopedTimer t(&time_taken_to_finalize_);
371 return UpdateState(FinalizeJobImpl(isolate), State::kSucceeded);
372 }
373
RetryOptimization(BailoutReason reason)374 CompilationJob::Status OptimizedCompilationJob::RetryOptimization(
375 BailoutReason reason) {
376 DCHECK(compilation_info_->IsOptimizing());
377 compilation_info_->RetryOptimization(reason);
378 return UpdateState(FAILED, State::kFailed);
379 }
380
AbortOptimization(BailoutReason reason)381 CompilationJob::Status OptimizedCompilationJob::AbortOptimization(
382 BailoutReason reason) {
383 DCHECK(compilation_info_->IsOptimizing());
384 compilation_info_->AbortOptimization(reason);
385 return UpdateState(FAILED, State::kFailed);
386 }
387
RecordCompilationStats(CompilationMode mode,Isolate * isolate) const388 void OptimizedCompilationJob::RecordCompilationStats(CompilationMode mode,
389 Isolate* isolate) const {
390 DCHECK(compilation_info()->IsOptimizing());
391 Handle<JSFunction> function = compilation_info()->closure();
392 double ms_creategraph = time_taken_to_prepare_.InMillisecondsF();
393 double ms_optimize = time_taken_to_execute_.InMillisecondsF();
394 double ms_codegen = time_taken_to_finalize_.InMillisecondsF();
395 CompilerTracer::TraceCompilationStats(
396 isolate, compilation_info(), ms_creategraph, ms_optimize, ms_codegen);
397 if (FLAG_trace_opt_stats) {
398 static double compilation_time = 0.0;
399 static int compiled_functions = 0;
400 static int code_size = 0;
401
402 compilation_time += (ms_creategraph + ms_optimize + ms_codegen);
403 compiled_functions++;
404 code_size += function->shared().SourceSize();
405 PrintF("Compiled: %d functions with %d byte source size in %fms.\n",
406 compiled_functions, code_size, compilation_time);
407 }
408 // Don't record samples from machines without high-resolution timers,
409 // as that can cause serious reporting issues. See the thread at
410 // http://g/chrome-metrics-team/NwwJEyL8odU/discussion for more details.
411 if (base::TimeTicks::IsHighResolution()) {
412 Counters* const counters = isolate->counters();
413 if (compilation_info()->is_osr()) {
414 counters->turbofan_osr_prepare()->AddSample(
415 static_cast<int>(time_taken_to_prepare_.InMicroseconds()));
416 counters->turbofan_osr_execute()->AddSample(
417 static_cast<int>(time_taken_to_execute_.InMicroseconds()));
418 counters->turbofan_osr_finalize()->AddSample(
419 static_cast<int>(time_taken_to_finalize_.InMicroseconds()));
420 counters->turbofan_osr_total_time()->AddSample(
421 static_cast<int>(ElapsedTime().InMicroseconds()));
422 } else {
423 counters->turbofan_optimize_prepare()->AddSample(
424 static_cast<int>(time_taken_to_prepare_.InMicroseconds()));
425 counters->turbofan_optimize_execute()->AddSample(
426 static_cast<int>(time_taken_to_execute_.InMicroseconds()));
427 counters->turbofan_optimize_finalize()->AddSample(
428 static_cast<int>(time_taken_to_finalize_.InMicroseconds()));
429 counters->turbofan_optimize_total_time()->AddSample(
430 static_cast<int>(ElapsedTime().InMicroseconds()));
431
432 // Compute foreground / background time.
433 base::TimeDelta time_background;
434 base::TimeDelta time_foreground =
435 time_taken_to_prepare_ + time_taken_to_finalize_;
436 switch (mode) {
437 case OptimizedCompilationJob::kConcurrent:
438 time_background += time_taken_to_execute_;
439 counters->turbofan_optimize_concurrent_total_time()->AddSample(
440 static_cast<int>(ElapsedTime().InMicroseconds()));
441 break;
442 case OptimizedCompilationJob::kSynchronous:
443 counters->turbofan_optimize_non_concurrent_total_time()->AddSample(
444 static_cast<int>(ElapsedTime().InMicroseconds()));
445 time_foreground += time_taken_to_execute_;
446 break;
447 }
448 counters->turbofan_optimize_total_background()->AddSample(
449 static_cast<int>(time_background.InMicroseconds()));
450 counters->turbofan_optimize_total_foreground()->AddSample(
451 static_cast<int>(time_foreground.InMicroseconds()));
452 }
453 counters->turbofan_ticks()->AddSample(static_cast<int>(
454 compilation_info()->tick_counter().CurrentTicks() / 1000));
455 }
456 }
457
RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,Isolate * isolate) const458 void OptimizedCompilationJob::RecordFunctionCompilation(
459 CodeEventListener::LogEventsAndTags tag, Isolate* isolate) const {
460 Handle<AbstractCode> abstract_code =
461 Handle<AbstractCode>::cast(compilation_info()->code());
462
463 double time_taken_ms = time_taken_to_prepare_.InMillisecondsF() +
464 time_taken_to_execute_.InMillisecondsF() +
465 time_taken_to_finalize_.InMillisecondsF();
466
467 Handle<Script> script(
468 Script::cast(compilation_info()->shared_info()->script()), isolate);
469 LogFunctionCompilation(tag, compilation_info()->shared_info(), script,
470 abstract_code, true, time_taken_ms, isolate);
471 }
472
473 // ----------------------------------------------------------------------------
474 // Local helper methods that make up the compilation pipeline.
475
476 namespace {
477
UseAsmWasm(FunctionLiteral * literal,bool asm_wasm_broken)478 bool UseAsmWasm(FunctionLiteral* literal, bool asm_wasm_broken) {
479 // Check whether asm.js validation is enabled.
480 if (!FLAG_validate_asm) return false;
481
482 // Modules that have validated successfully, but were subsequently broken by
483 // invalid module instantiation attempts are off limit forever.
484 if (asm_wasm_broken) return false;
485
486 // In stress mode we want to run the validator on everything.
487 if (FLAG_stress_validate_asm) return true;
488
489 // In general, we respect the "use asm" directive.
490 return literal->scope()->IsAsmModule();
491 }
492
InstallInterpreterTrampolineCopy(Isolate * isolate,Handle<SharedFunctionInfo> shared_info)493 void InstallInterpreterTrampolineCopy(Isolate* isolate,
494 Handle<SharedFunctionInfo> shared_info) {
495 DCHECK(FLAG_interpreted_frames_native_stack);
496 if (!shared_info->function_data(kAcquireLoad).IsBytecodeArray()) {
497 DCHECK(!shared_info->HasBytecodeArray());
498 return;
499 }
500 Handle<BytecodeArray> bytecode_array(shared_info->GetBytecodeArray(),
501 isolate);
502
503 Handle<Code> code = isolate->factory()->CopyCode(Handle<Code>::cast(
504 isolate->factory()->interpreter_entry_trampoline_for_profiling()));
505
506 Handle<InterpreterData> interpreter_data =
507 Handle<InterpreterData>::cast(isolate->factory()->NewStruct(
508 INTERPRETER_DATA_TYPE, AllocationType::kOld));
509
510 interpreter_data->set_bytecode_array(*bytecode_array);
511 interpreter_data->set_interpreter_trampoline(*code);
512
513 shared_info->set_interpreter_data(*interpreter_data);
514
515 Handle<Script> script(Script::cast(shared_info->script()), isolate);
516 Handle<AbstractCode> abstract_code = Handle<AbstractCode>::cast(code);
517 int line_num =
518 Script::GetLineNumber(script, shared_info->StartPosition()) + 1;
519 int column_num =
520 Script::GetColumnNumber(script, shared_info->StartPosition()) + 1;
521 Handle<String> script_name =
522 handle(script->name().IsString() ? String::cast(script->name())
523 : ReadOnlyRoots(isolate).empty_string(),
524 isolate);
525 CodeEventListener::LogEventsAndTags log_tag = Logger::ToNativeByScript(
526 CodeEventListener::INTERPRETED_FUNCTION_TAG, *script);
527 PROFILE(isolate, CodeCreateEvent(log_tag, abstract_code, shared_info,
528 script_name, line_num, column_num));
529 }
530
531 template <typename LocalIsolate>
InstallUnoptimizedCode(UnoptimizedCompilationInfo * compilation_info,Handle<SharedFunctionInfo> shared_info,LocalIsolate * isolate)532 void InstallUnoptimizedCode(UnoptimizedCompilationInfo* compilation_info,
533 Handle<SharedFunctionInfo> shared_info,
534 LocalIsolate* isolate) {
535 if (compilation_info->has_bytecode_array()) {
536 DCHECK(!shared_info->HasBytecodeArray()); // Only compiled once.
537 DCHECK(!compilation_info->has_asm_wasm_data());
538 DCHECK(!shared_info->HasFeedbackMetadata());
539
540 // If the function failed asm-wasm compilation, mark asm_wasm as broken
541 // to ensure we don't try to compile as asm-wasm.
542 if (compilation_info->literal()->scope()->IsAsmModule()) {
543 shared_info->set_is_asm_wasm_broken(true);
544 }
545
546 shared_info->set_bytecode_array(*compilation_info->bytecode_array());
547
548 Handle<FeedbackMetadata> feedback_metadata = FeedbackMetadata::New(
549 isolate, compilation_info->feedback_vector_spec());
550 shared_info->set_feedback_metadata(*feedback_metadata);
551 } else {
552 DCHECK(compilation_info->has_asm_wasm_data());
553 // We should only have asm/wasm data when finalizing on the main thread.
554 DCHECK((std::is_same<LocalIsolate, Isolate>::value));
555 shared_info->set_asm_wasm_data(*compilation_info->asm_wasm_data());
556 shared_info->set_feedback_metadata(
557 ReadOnlyRoots(isolate).empty_feedback_metadata());
558 }
559 }
560
LogUnoptimizedCompilation(Isolate * isolate,Handle<SharedFunctionInfo> shared_info,UnoptimizedCompileFlags flags,base::TimeDelta time_taken_to_execute,base::TimeDelta time_taken_to_finalize)561 void LogUnoptimizedCompilation(Isolate* isolate,
562 Handle<SharedFunctionInfo> shared_info,
563 UnoptimizedCompileFlags flags,
564 base::TimeDelta time_taken_to_execute,
565 base::TimeDelta time_taken_to_finalize) {
566 CodeEventListener::LogEventsAndTags log_tag;
567 if (flags.is_toplevel()) {
568 log_tag = flags.is_eval() ? CodeEventListener::EVAL_TAG
569 : CodeEventListener::SCRIPT_TAG;
570 } else {
571 log_tag = flags.is_lazy_compile() ? CodeEventListener::LAZY_COMPILE_TAG
572 : CodeEventListener::FUNCTION_TAG;
573 }
574
575 RecordUnoptimizedFunctionCompilation(isolate, log_tag, shared_info,
576 time_taken_to_execute,
577 time_taken_to_finalize);
578 RecordUnoptimizedCompilationStats(isolate, shared_info);
579 }
580
581 template <typename LocalIsolate>
EnsureSharedFunctionInfosArrayOnScript(Handle<Script> script,ParseInfo * parse_info,LocalIsolate * isolate)582 void EnsureSharedFunctionInfosArrayOnScript(Handle<Script> script,
583 ParseInfo* parse_info,
584 LocalIsolate* isolate) {
585 DCHECK(parse_info->flags().is_toplevel());
586 if (script->shared_function_infos().length() > 0) {
587 DCHECK_EQ(script->shared_function_infos().length(),
588 parse_info->max_function_literal_id() + 1);
589 return;
590 }
591 Handle<WeakFixedArray> infos(isolate->factory()->NewWeakFixedArray(
592 parse_info->max_function_literal_id() + 1, AllocationType::kOld));
593 script->set_shared_function_infos(*infos);
594 }
595
UpdateSharedFunctionFlagsAfterCompilation(FunctionLiteral * literal,SharedFunctionInfo shared_info)596 void UpdateSharedFunctionFlagsAfterCompilation(FunctionLiteral* literal,
597 SharedFunctionInfo shared_info) {
598 DCHECK_EQ(shared_info.language_mode(), literal->language_mode());
599
600 shared_info.set_has_duplicate_parameters(literal->has_duplicate_parameters());
601 shared_info.set_is_oneshot_iife(literal->is_oneshot_iife());
602 shared_info.UpdateAndFinalizeExpectedNofPropertiesFromEstimate(literal);
603 if (literal->dont_optimize_reason() != BailoutReason::kNoReason) {
604 shared_info.DisableOptimization(literal->dont_optimize_reason());
605 }
606
607 shared_info.set_class_scope_has_private_brand(
608 literal->class_scope_has_private_brand());
609 shared_info.set_has_static_private_methods_or_accessors(
610 literal->has_static_private_methods_or_accessors());
611
612 shared_info.SetScopeInfo(*literal->scope()->scope_info());
613 }
614
615 // Finalize a single compilation job. This function can return
616 // RETRY_ON_MAIN_THREAD if the job cannot be finalized off-thread, in which case
617 // it should be safe to call it again on the main thread with the same job.
618 template <typename LocalIsolate>
FinalizeSingleUnoptimizedCompilationJob(UnoptimizedCompilationJob * job,Handle<SharedFunctionInfo> shared_info,LocalIsolate * isolate,FinalizeUnoptimizedCompilationDataList * finalize_unoptimized_compilation_data_list)619 CompilationJob::Status FinalizeSingleUnoptimizedCompilationJob(
620 UnoptimizedCompilationJob* job, Handle<SharedFunctionInfo> shared_info,
621 LocalIsolate* isolate,
622 FinalizeUnoptimizedCompilationDataList*
623 finalize_unoptimized_compilation_data_list) {
624 UnoptimizedCompilationInfo* compilation_info = job->compilation_info();
625
626 CompilationJob::Status status = job->FinalizeJob(shared_info, isolate);
627 if (status == CompilationJob::SUCCEEDED) {
628 InstallUnoptimizedCode(compilation_info, shared_info, isolate);
629
630 MaybeHandle<CoverageInfo> coverage_info;
631 if (compilation_info->has_coverage_info() &&
632 !shared_info->HasCoverageInfo()) {
633 coverage_info = compilation_info->coverage_info();
634 }
635
636 finalize_unoptimized_compilation_data_list->emplace_back(
637 isolate, shared_info, coverage_info, job->time_taken_to_execute(),
638 job->time_taken_to_finalize());
639 }
640 DCHECK_IMPLIES(
641 status == CompilationJob::RETRY_ON_MAIN_THREAD,
642 (std::is_same<LocalIsolate, v8::internal::LocalIsolate>::value));
643 return status;
644 }
645
646 std::unique_ptr<UnoptimizedCompilationJob>
ExecuteSingleUnoptimizedCompilationJob(ParseInfo * parse_info,FunctionLiteral * literal,AccountingAllocator * allocator,std::vector<FunctionLiteral * > * eager_inner_literals)647 ExecuteSingleUnoptimizedCompilationJob(
648 ParseInfo* parse_info, FunctionLiteral* literal,
649 AccountingAllocator* allocator,
650 std::vector<FunctionLiteral*>* eager_inner_literals) {
651 if (UseAsmWasm(literal, parse_info->flags().is_asm_wasm_broken())) {
652 std::unique_ptr<UnoptimizedCompilationJob> asm_job(
653 AsmJs::NewCompilationJob(parse_info, literal, allocator));
654 if (asm_job->ExecuteJob() == CompilationJob::SUCCEEDED) {
655 return asm_job;
656 }
657 // asm.js validation failed, fall through to standard unoptimized compile.
658 // Note: we rely on the fact that AsmJs jobs have done all validation in the
659 // PrepareJob and ExecuteJob phases and can't fail in FinalizeJob with
660 // with a validation error or another error that could be solve by falling
661 // through to standard unoptimized compile.
662 }
663 std::unique_ptr<UnoptimizedCompilationJob> job(
664 interpreter::Interpreter::NewCompilationJob(
665 parse_info, literal, allocator, eager_inner_literals));
666
667 if (job->ExecuteJob() != CompilationJob::SUCCEEDED) {
668 // Compilation failed, return null.
669 return std::unique_ptr<UnoptimizedCompilationJob>();
670 }
671
672 return job;
673 }
674
RecursivelyExecuteUnoptimizedCompilationJobs(ParseInfo * parse_info,FunctionLiteral * literal,AccountingAllocator * allocator,UnoptimizedCompilationJobList * function_jobs)675 bool RecursivelyExecuteUnoptimizedCompilationJobs(
676 ParseInfo* parse_info, FunctionLiteral* literal,
677 AccountingAllocator* allocator,
678 UnoptimizedCompilationJobList* function_jobs) {
679 std::vector<FunctionLiteral*> eager_inner_literals;
680 std::unique_ptr<UnoptimizedCompilationJob> job =
681 ExecuteSingleUnoptimizedCompilationJob(parse_info, literal, allocator,
682 &eager_inner_literals);
683
684 if (!job) return false;
685
686 // Recursively compile eager inner literals.
687 for (FunctionLiteral* inner_literal : eager_inner_literals) {
688 if (!RecursivelyExecuteUnoptimizedCompilationJobs(
689 parse_info, inner_literal, allocator, function_jobs)) {
690 return false;
691 }
692 }
693
694 function_jobs->emplace_front(std::move(job));
695 return true;
696 }
697
698 template <typename LocalIsolate>
IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(LocalIsolate * isolate,Handle<SharedFunctionInfo> outer_shared_info,Handle<Script> script,ParseInfo * parse_info,AccountingAllocator * allocator,IsCompiledScope * is_compiled_scope,FinalizeUnoptimizedCompilationDataList * finalize_unoptimized_compilation_data_list,DeferredFinalizationJobDataList * jobs_to_retry_finalization_on_main_thread)699 bool IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
700 LocalIsolate* isolate, Handle<SharedFunctionInfo> outer_shared_info,
701 Handle<Script> script, ParseInfo* parse_info,
702 AccountingAllocator* allocator, IsCompiledScope* is_compiled_scope,
703 FinalizeUnoptimizedCompilationDataList*
704 finalize_unoptimized_compilation_data_list,
705 DeferredFinalizationJobDataList*
706 jobs_to_retry_finalization_on_main_thread) {
707 DeclarationScope::AllocateScopeInfos(parse_info, isolate);
708
709 std::vector<FunctionLiteral*> functions_to_compile;
710 functions_to_compile.push_back(parse_info->literal());
711
712 while (!functions_to_compile.empty()) {
713 FunctionLiteral* literal = functions_to_compile.back();
714 functions_to_compile.pop_back();
715 Handle<SharedFunctionInfo> shared_info =
716 Compiler::GetSharedFunctionInfo(literal, script, isolate);
717 if (shared_info->is_compiled()) continue;
718
719 std::unique_ptr<UnoptimizedCompilationJob> job =
720 ExecuteSingleUnoptimizedCompilationJob(parse_info, literal, allocator,
721 &functions_to_compile);
722 if (!job) return false;
723
724 UpdateSharedFunctionFlagsAfterCompilation(literal, *shared_info);
725
726 auto finalization_status = FinalizeSingleUnoptimizedCompilationJob(
727 job.get(), shared_info, isolate,
728 finalize_unoptimized_compilation_data_list);
729
730 switch (finalization_status) {
731 case CompilationJob::SUCCEEDED:
732 if (shared_info.is_identical_to(outer_shared_info)) {
733 // Ensure that the top level function is retained.
734 *is_compiled_scope = shared_info->is_compiled_scope(isolate);
735 DCHECK(is_compiled_scope->is_compiled());
736 }
737 break;
738
739 case CompilationJob::FAILED:
740 return false;
741
742 case CompilationJob::RETRY_ON_MAIN_THREAD:
743 // This should not happen on the main thread.
744 DCHECK((!std::is_same<LocalIsolate, Isolate>::value));
745 DCHECK_NOT_NULL(jobs_to_retry_finalization_on_main_thread);
746
747 // Clear the literal and ParseInfo to prevent further attempts to access
748 // them.
749 job->compilation_info()->ClearLiteral();
750 job->ClearParseInfo();
751 jobs_to_retry_finalization_on_main_thread->emplace_back(
752 isolate, shared_info, std::move(job));
753 break;
754 }
755 }
756
757 // Report any warnings generated during compilation.
758 if (parse_info->pending_error_handler()->has_pending_warnings()) {
759 parse_info->pending_error_handler()->PrepareWarnings(isolate);
760 }
761
762 return true;
763 }
764
FinalizeAllUnoptimizedCompilationJobs(ParseInfo * parse_info,Isolate * isolate,Handle<Script> script,UnoptimizedCompilationJobList * compilation_jobs,FinalizeUnoptimizedCompilationDataList * finalize_unoptimized_compilation_data_list)765 bool FinalizeAllUnoptimizedCompilationJobs(
766 ParseInfo* parse_info, Isolate* isolate, Handle<Script> script,
767 UnoptimizedCompilationJobList* compilation_jobs,
768 FinalizeUnoptimizedCompilationDataList*
769 finalize_unoptimized_compilation_data_list) {
770 DCHECK(AllowCompilation::IsAllowed(isolate));
771 DCHECK(!compilation_jobs->empty());
772
773 // TODO(rmcilroy): Clear native context in debug once AsmJS generates doesn't
774 // rely on accessing native context during finalization.
775
776 // Allocate scope infos for the literal.
777 DeclarationScope::AllocateScopeInfos(parse_info, isolate);
778
779 // Finalize the functions' compilation jobs.
780 for (auto&& job : *compilation_jobs) {
781 FunctionLiteral* literal = job->compilation_info()->literal();
782 Handle<SharedFunctionInfo> shared_info =
783 Compiler::GetSharedFunctionInfo(literal, script, isolate);
784 // The inner function might be compiled already if compiling for debug.
785 if (shared_info->is_compiled()) continue;
786 UpdateSharedFunctionFlagsAfterCompilation(literal, *shared_info);
787 if (FinalizeSingleUnoptimizedCompilationJob(
788 job.get(), shared_info, isolate,
789 finalize_unoptimized_compilation_data_list) !=
790 CompilationJob::SUCCEEDED) {
791 return false;
792 }
793 }
794
795 // Report any warnings generated during compilation.
796 if (parse_info->pending_error_handler()->has_pending_warnings()) {
797 parse_info->pending_error_handler()->PrepareWarnings(isolate);
798 }
799
800 return true;
801 }
802
FinalizeDeferredUnoptimizedCompilationJobs(Isolate * isolate,Handle<Script> script,DeferredFinalizationJobDataList * deferred_jobs,PendingCompilationErrorHandler * pending_error_handler,FinalizeUnoptimizedCompilationDataList * finalize_unoptimized_compilation_data_list)803 bool FinalizeDeferredUnoptimizedCompilationJobs(
804 Isolate* isolate, Handle<Script> script,
805 DeferredFinalizationJobDataList* deferred_jobs,
806 PendingCompilationErrorHandler* pending_error_handler,
807 FinalizeUnoptimizedCompilationDataList*
808 finalize_unoptimized_compilation_data_list) {
809 DCHECK(AllowCompilation::IsAllowed(isolate));
810
811 if (deferred_jobs->empty()) return true;
812
813 // TODO(rmcilroy): Clear native context in debug once AsmJS generates doesn't
814 // rely on accessing native context during finalization.
815
816 // Finalize the deferred compilation jobs.
817 for (auto&& job : *deferred_jobs) {
818 Handle<SharedFunctionInfo> shared_info = job.function_handle();
819 if (FinalizeSingleUnoptimizedCompilationJob(
820 job.job(), shared_info, isolate,
821 finalize_unoptimized_compilation_data_list) !=
822 CompilationJob::SUCCEEDED) {
823 return false;
824 }
825 }
826
827 // Report any warnings generated during deferred finalization.
828 if (pending_error_handler->has_pending_warnings()) {
829 pending_error_handler->PrepareWarnings(isolate);
830 }
831
832 return true;
833 }
834
GetCodeFromOptimizedCodeCache(Handle<JSFunction> function,BailoutId osr_offset,CodeKind code_kind)835 V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache(
836 Handle<JSFunction> function, BailoutId osr_offset, CodeKind code_kind) {
837 RuntimeCallTimerScope runtimeTimer(
838 function->GetIsolate(),
839 RuntimeCallCounterId::kCompileGetFromOptimizedCodeMap);
840 Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
841 Isolate* isolate = function->GetIsolate();
842 DisallowHeapAllocation no_gc;
843 Code code;
844 if (osr_offset.IsNone() && function->has_feedback_vector()) {
845 FeedbackVector feedback_vector = function->feedback_vector();
846 feedback_vector.EvictOptimizedCodeMarkedForDeoptimization(
847 function->shared(), "GetCodeFromOptimizedCodeCache");
848 code = feedback_vector.optimized_code();
849 } else if (!osr_offset.IsNone()) {
850 code = function->context()
851 .native_context()
852 .GetOSROptimizedCodeCache()
853 .GetOptimizedCode(shared, osr_offset, isolate);
854 }
855 DCHECK_IMPLIES(!code.is_null(), code.kind() <= code_kind);
856 if (!code.is_null() && code.kind() == code_kind) {
857 // Caching of optimized code enabled and optimized code found.
858 DCHECK(!code.marked_for_deoptimization());
859 DCHECK(function->shared().is_compiled());
860 DCHECK(CodeKindIsStoredInOptimizedCodeCache(code.kind()));
861 DCHECK_IMPLIES(!osr_offset.IsNone(), CodeKindCanOSR(code.kind()));
862 return Handle<Code>(code, isolate);
863 }
864 return MaybeHandle<Code>();
865 }
866
ClearOptimizedCodeCache(OptimizedCompilationInfo * compilation_info)867 void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) {
868 DCHECK(UsesOptimizationMarker(compilation_info->code_kind()));
869 Handle<JSFunction> function = compilation_info->closure();
870 if (compilation_info->osr_offset().IsNone()) {
871 Handle<FeedbackVector> vector =
872 handle(function->feedback_vector(), function->GetIsolate());
873 vector->ClearOptimizationMarker();
874 }
875 }
876
InsertCodeIntoOptimizedCodeCache(OptimizedCompilationInfo * compilation_info)877 void InsertCodeIntoOptimizedCodeCache(
878 OptimizedCompilationInfo* compilation_info) {
879 const CodeKind kind = compilation_info->code_kind();
880 if (!CodeKindIsStoredInOptimizedCodeCache(kind)) {
881 if (UsesOptimizationMarker(kind)) {
882 ClearOptimizedCodeCache(compilation_info);
883 }
884 return;
885 }
886
887 if (compilation_info->function_context_specializing()) {
888 // Function context specialization folds-in the function context, so no
889 // sharing can occur. Make sure the optimized code cache is cleared.
890 ClearOptimizedCodeCache(compilation_info);
891 return;
892 }
893
894 // Cache optimized code.
895 Handle<Code> code = compilation_info->code();
896 Handle<JSFunction> function = compilation_info->closure();
897 Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
898 Handle<NativeContext> native_context(function->context().native_context(),
899 function->GetIsolate());
900 if (compilation_info->osr_offset().IsNone()) {
901 Handle<FeedbackVector> vector =
902 handle(function->feedback_vector(), function->GetIsolate());
903 FeedbackVector::SetOptimizedCode(vector, code);
904 } else {
905 DCHECK(CodeKindCanOSR(kind));
906 OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
907 compilation_info->osr_offset());
908 }
909 }
910
InsertCodeIntoCompilationCache(Isolate * isolate,OptimizedCompilationInfo * info)911 void InsertCodeIntoCompilationCache(Isolate* isolate,
912 OptimizedCompilationInfo* info) {
913 if (!CodeKindIsNativeContextIndependentJSFunction(info->code_kind())) return;
914
915 DCHECK(info->osr_offset().IsNone());
916
917 Handle<Code> code = info->code();
918 DCHECK(!info->function_context_specializing());
919
920 Handle<SharedFunctionInfo> sfi = info->shared_info();
921 CompilationCache* cache = isolate->compilation_cache();
922 cache->PutCode(sfi, code);
923 DCHECK(!cache->LookupCode(sfi).is_null());
924
925 sfi->set_may_have_cached_code(true);
926
927 if (FLAG_trace_turbo_nci) CompilationCacheCode::TraceInsertion(sfi, code);
928 }
929
GetCodeFromCompilationCache(Isolate * isolate,Handle<SharedFunctionInfo> shared)930 V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromCompilationCache(
931 Isolate* isolate, Handle<SharedFunctionInfo> shared) {
932 if (!shared->may_have_cached_code()) return {};
933 return shared->TryGetCachedCode(isolate);
934 }
935
936 // Runs PrepareJob in the proper compilation & canonical scopes. Handles will be
937 // allocated in a persistent handle scope that is detached and handed off to the
938 // {compilation_info} after PrepareJob.
PrepareJobWithHandleScope(OptimizedCompilationJob * job,Isolate * isolate,OptimizedCompilationInfo * compilation_info)939 bool PrepareJobWithHandleScope(OptimizedCompilationJob* job, Isolate* isolate,
940 OptimizedCompilationInfo* compilation_info) {
941 CompilationHandleScope compilation(isolate, compilation_info);
942 CanonicalHandleScope canonical(isolate, compilation_info);
943 compilation_info->ReopenHandlesInNewHandleScope(isolate);
944 return job->PrepareJob(isolate) == CompilationJob::SUCCEEDED;
945 }
946
GetOptimizedCodeNow(OptimizedCompilationJob * job,Isolate * isolate,OptimizedCompilationInfo * compilation_info)947 bool GetOptimizedCodeNow(OptimizedCompilationJob* job, Isolate* isolate,
948 OptimizedCompilationInfo* compilation_info) {
949 TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
950 RuntimeCallTimerScope runtimeTimer(
951 isolate, RuntimeCallCounterId::kOptimizeNonConcurrent);
952 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
953 "V8.OptimizeNonConcurrent");
954
955 if (!PrepareJobWithHandleScope(job, isolate, compilation_info)) {
956 CompilerTracer::TraceAbortedJob(isolate, compilation_info);
957 return false;
958 }
959
960 {
961 LocalIsolate local_isolate(isolate, ThreadKind::kMain);
962 if (job->ExecuteJob(isolate->counters()->runtime_call_stats(),
963 &local_isolate)) {
964 CompilerTracer::TraceAbortedJob(isolate, compilation_info);
965 return false;
966 }
967 }
968
969 if (job->FinalizeJob(isolate) != CompilationJob::SUCCEEDED) {
970 CompilerTracer::TraceAbortedJob(isolate, compilation_info);
971 return false;
972 }
973
974 // Success!
975 job->RecordCompilationStats(OptimizedCompilationJob::kSynchronous, isolate);
976 DCHECK(!isolate->has_pending_exception());
977 InsertCodeIntoOptimizedCodeCache(compilation_info);
978 job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, isolate);
979 return true;
980 }
981
GetOptimizedCodeLater(std::unique_ptr<OptimizedCompilationJob> job,Isolate * isolate,OptimizedCompilationInfo * compilation_info,CodeKind code_kind,Handle<JSFunction> function)982 bool GetOptimizedCodeLater(std::unique_ptr<OptimizedCompilationJob> job,
983 Isolate* isolate,
984 OptimizedCompilationInfo* compilation_info,
985 CodeKind code_kind, Handle<JSFunction> function) {
986 if (!isolate->optimizing_compile_dispatcher()->IsQueueAvailable()) {
987 if (FLAG_trace_concurrent_recompilation) {
988 PrintF(" ** Compilation queue full, will retry optimizing ");
989 compilation_info->closure()->ShortPrint();
990 PrintF(" later.\n");
991 }
992 return false;
993 }
994
995 if (isolate->heap()->HighMemoryPressure()) {
996 if (FLAG_trace_concurrent_recompilation) {
997 PrintF(" ** High memory pressure, will retry optimizing ");
998 compilation_info->closure()->ShortPrint();
999 PrintF(" later.\n");
1000 }
1001 return false;
1002 }
1003
1004 TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
1005 RuntimeCallTimerScope runtimeTimer(
1006 isolate, RuntimeCallCounterId::kOptimizeConcurrentPrepare);
1007 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1008 "V8.OptimizeConcurrentPrepare");
1009
1010 if (!PrepareJobWithHandleScope(job.get(), isolate, compilation_info)) {
1011 return false;
1012 }
1013
1014 // The background recompile will own this job.
1015 isolate->optimizing_compile_dispatcher()->QueueForOptimization(job.get());
1016 job.release();
1017
1018 if (FLAG_trace_concurrent_recompilation) {
1019 PrintF(" ** Queued ");
1020 compilation_info->closure()->ShortPrint();
1021 PrintF(" for concurrent optimization.\n");
1022 }
1023
1024 if (CodeKindIsStoredInOptimizedCodeCache(code_kind)) {
1025 function->SetOptimizationMarker(OptimizationMarker::kInOptimizationQueue);
1026 }
1027
1028 // Note: Usually the active tier is expected to be Ignition or NCI at this
1029 // point (in other words we don't expect to optimize if the function is
1030 // already TF-optimized). There is a special case for OSR though, for which
1031 // we *can* reach this point even if we've already generated non-OSR'd TF
1032 // code.
1033 DCHECK(function->shared().HasBytecodeArray());
1034 return true;
1035 }
1036
1037 // Returns the code object at which execution continues after a concurrent
1038 // optimization job has been started (but not finished).
ContinuationForConcurrentOptimization(Isolate * isolate,Handle<JSFunction> function)1039 Handle<Code> ContinuationForConcurrentOptimization(
1040 Isolate* isolate, Handle<JSFunction> function) {
1041 Handle<Code> cached_code;
1042 if (FLAG_turbo_nci && function->NextTier() == CodeKindForTopTier() &&
1043 GetCodeFromCompilationCache(isolate, handle(function->shared(), isolate))
1044 .ToHandle(&cached_code)) {
1045 // Tiering up to Turbofan and cached optimized code exists. Continue
1046 // execution there until TF optimization has finished.
1047 return cached_code;
1048 } else if (FLAG_turboprop_as_midtier &&
1049 function->HasAvailableOptimizedCode()) {
1050 DCHECK(function->NextTier() == CodeKind::TURBOFAN);
1051 // It is possible that we have marked a closure for TurboFan optimization
1052 // but the marker is processed by another closure that doesn't have
1053 // optimized code yet. So heal the closure here and return the optimized
1054 // code.
1055 if (!function->HasAttachedOptimizedCode()) {
1056 DCHECK(function->feedback_vector().has_optimized_code());
1057 function->set_code(function->feedback_vector().optimized_code());
1058 }
1059 return handle(function->code(), isolate);
1060 }
1061 return BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
1062 }
1063
GetOptimizedCode(Handle<JSFunction> function,ConcurrencyMode mode,CodeKind code_kind,BailoutId osr_offset=BailoutId::None (),JavaScriptFrame * osr_frame=nullptr)1064 MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
1065 ConcurrencyMode mode, CodeKind code_kind,
1066 BailoutId osr_offset = BailoutId::None(),
1067 JavaScriptFrame* osr_frame = nullptr) {
1068 DCHECK(CodeKindIsOptimizedJSFunction(code_kind));
1069
1070 Isolate* isolate = function->GetIsolate();
1071 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
1072
1073 // Make sure we clear the optimization marker on the function so that we
1074 // don't try to re-optimize.
1075 // If compiling for NCI caching only (which does not use the optimization
1076 // marker), don't touch the marker to avoid interfering with Turbofan
1077 // compilation.
1078 if (UsesOptimizationMarker(code_kind) && function->HasOptimizationMarker()) {
1079 function->ClearOptimizationMarker();
1080 }
1081
1082 if (shared->optimization_disabled() &&
1083 shared->disable_optimization_reason() == BailoutReason::kNeverOptimize) {
1084 return {};
1085 }
1086
1087 // Do not optimize when debugger needs to hook into every call.
1088 if (isolate->debug()->needs_check_on_function_call()) return {};
1089
1090 // Do not use TurboFan if we need to be able to set break points.
1091 if (shared->HasBreakInfo()) return {};
1092
1093 // Do not use TurboFan if optimization is disabled or function doesn't pass
1094 // turbo_filter.
1095 if (!FLAG_opt || !shared->PassesFilter(FLAG_turbo_filter)) return {};
1096
1097 // If code was pending optimization for testing, remove the entry from the
1098 // table that was preventing the bytecode from being flushed.
1099 if (V8_UNLIKELY(FLAG_testing_d8_test_runner)) {
1100 PendingOptimizationTable::FunctionWasOptimized(isolate, function);
1101 }
1102
1103 // Check the optimized code cache (stored on the SharedFunctionInfo).
1104 if (CodeKindIsStoredInOptimizedCodeCache(code_kind)) {
1105 Handle<Code> cached_code;
1106 if (GetCodeFromOptimizedCodeCache(function, osr_offset, code_kind)
1107 .ToHandle(&cached_code)) {
1108 CompilerTracer::TraceOptimizedCodeCacheHit(isolate, function, osr_offset,
1109 code_kind);
1110 return cached_code;
1111 }
1112 }
1113
1114 // Reset profiler ticks, function is no longer considered hot.
1115 DCHECK(shared->is_compiled());
1116 function->feedback_vector().set_profiler_ticks(0);
1117
1118 // Check the compilation cache (stored on the Isolate, shared between native
1119 // contexts).
1120 if (CodeKindIsNativeContextIndependentJSFunction(code_kind)) {
1121 DCHECK(osr_offset.IsNone());
1122 DCHECK(FLAG_turbo_nci_as_midtier || !FLAG_turbo_nci_delayed_codegen ||
1123 shared->has_optimized_at_least_once());
1124
1125 Handle<Code> cached_code;
1126 if (GetCodeFromCompilationCache(isolate, shared).ToHandle(&cached_code)) {
1127 CHECK_EQ(cached_code->kind(), CodeKind::NATIVE_CONTEXT_INDEPENDENT);
1128 if (FLAG_trace_turbo_nci) {
1129 CompilationCacheCode::TraceHit(shared, cached_code);
1130 }
1131 return cached_code;
1132 }
1133 }
1134
1135 VMState<COMPILER> state(isolate);
1136 TimerEventScope<TimerEventOptimizeCode> optimize_code_timer(isolate);
1137 RuntimeCallTimerScope runtimeTimer(isolate,
1138 RuntimeCallCounterId::kOptimizeCode);
1139 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.OptimizeCode");
1140
1141 DCHECK(!isolate->has_pending_exception());
1142 PostponeInterruptsScope postpone(isolate);
1143 bool has_script = shared->script().IsScript();
1144 // BUG(5946): This DCHECK is necessary to make certain that we won't
1145 // tolerate the lack of a script without bytecode.
1146 DCHECK_IMPLIES(!has_script, shared->HasBytecodeArray());
1147 std::unique_ptr<OptimizedCompilationJob> job(
1148 compiler::Pipeline::NewCompilationJob(isolate, function, code_kind,
1149 has_script, osr_offset, osr_frame));
1150 OptimizedCompilationInfo* compilation_info = job->compilation_info();
1151
1152 // Prepare the job and launch concurrent compilation, or compile now.
1153 if (mode == ConcurrencyMode::kConcurrent) {
1154 if (GetOptimizedCodeLater(std::move(job), isolate, compilation_info,
1155 code_kind, function)) {
1156 return ContinuationForConcurrentOptimization(isolate, function);
1157 }
1158 } else {
1159 DCHECK_EQ(mode, ConcurrencyMode::kNotConcurrent);
1160 if (GetOptimizedCodeNow(job.get(), isolate, compilation_info)) {
1161 InsertCodeIntoCompilationCache(isolate, compilation_info);
1162 return compilation_info->code();
1163 }
1164 }
1165
1166 if (isolate->has_pending_exception()) isolate->clear_pending_exception();
1167 return {};
1168 }
1169
FailAndClearPendingException(Isolate * isolate)1170 bool FailAndClearPendingException(Isolate* isolate) {
1171 isolate->clear_pending_exception();
1172 return false;
1173 }
1174
1175 template <typename LocalIsolate>
PreparePendingException(LocalIsolate * isolate,ParseInfo * parse_info)1176 bool PreparePendingException(LocalIsolate* isolate, ParseInfo* parse_info) {
1177 if (parse_info->pending_error_handler()->has_pending_error()) {
1178 parse_info->pending_error_handler()->PrepareErrors(
1179 isolate, parse_info->ast_value_factory());
1180 }
1181 return false;
1182 }
1183
FailWithPreparedPendingException(Isolate * isolate,Handle<Script> script,const PendingCompilationErrorHandler * pending_error_handler)1184 bool FailWithPreparedPendingException(
1185 Isolate* isolate, Handle<Script> script,
1186 const PendingCompilationErrorHandler* pending_error_handler) {
1187 if (!isolate->has_pending_exception()) {
1188 if (pending_error_handler->has_pending_error()) {
1189 pending_error_handler->ReportErrors(isolate, script);
1190 } else {
1191 isolate->StackOverflow();
1192 }
1193 }
1194 return false;
1195 }
1196
FailWithPendingException(Isolate * isolate,Handle<Script> script,ParseInfo * parse_info,Compiler::ClearExceptionFlag flag)1197 bool FailWithPendingException(Isolate* isolate, Handle<Script> script,
1198 ParseInfo* parse_info,
1199 Compiler::ClearExceptionFlag flag) {
1200 if (flag == Compiler::CLEAR_EXCEPTION) {
1201 return FailAndClearPendingException(isolate);
1202 }
1203
1204 PreparePendingException(isolate, parse_info);
1205 return FailWithPreparedPendingException(isolate, script,
1206 parse_info->pending_error_handler());
1207 }
1208
FinalizeUnoptimizedCompilation(Isolate * isolate,Handle<Script> script,const UnoptimizedCompileFlags & flags,const UnoptimizedCompileState * compile_state,const FinalizeUnoptimizedCompilationDataList & finalize_unoptimized_compilation_data_list)1209 void FinalizeUnoptimizedCompilation(
1210 Isolate* isolate, Handle<Script> script,
1211 const UnoptimizedCompileFlags& flags,
1212 const UnoptimizedCompileState* compile_state,
1213 const FinalizeUnoptimizedCompilationDataList&
1214 finalize_unoptimized_compilation_data_list) {
1215 if (compile_state->pending_error_handler()->has_pending_warnings()) {
1216 compile_state->pending_error_handler()->ReportWarnings(isolate, script);
1217 }
1218
1219 bool need_source_positions = FLAG_stress_lazy_source_positions ||
1220 (!flags.collect_source_positions() &&
1221 isolate->NeedsSourcePositionsForProfiling());
1222
1223 for (const auto& finalize_data : finalize_unoptimized_compilation_data_list) {
1224 Handle<SharedFunctionInfo> shared_info = finalize_data.function_handle();
1225 // It's unlikely, but possible, that the bytecode was flushed between being
1226 // allocated and now, so guard against that case, and against it being
1227 // flushed in the middle of this loop.
1228 IsCompiledScope is_compiled_scope(*shared_info, isolate);
1229 if (!is_compiled_scope.is_compiled()) continue;
1230
1231 if (need_source_positions) {
1232 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared_info);
1233 }
1234 if (FLAG_interpreted_frames_native_stack) {
1235 InstallInterpreterTrampolineCopy(isolate, shared_info);
1236 }
1237 Handle<CoverageInfo> coverage_info;
1238 if (finalize_data.coverage_info().ToHandle(&coverage_info)) {
1239 isolate->debug()->InstallCoverageInfo(shared_info, coverage_info);
1240 }
1241
1242 LogUnoptimizedCompilation(isolate, shared_info, flags,
1243 finalize_data.time_taken_to_execute(),
1244 finalize_data.time_taken_to_finalize());
1245 }
1246 }
1247
FinalizeUnoptimizedScriptCompilation(Isolate * isolate,Handle<Script> script,const UnoptimizedCompileFlags & flags,const UnoptimizedCompileState * compile_state,const FinalizeUnoptimizedCompilationDataList & finalize_unoptimized_compilation_data_list)1248 void FinalizeUnoptimizedScriptCompilation(
1249 Isolate* isolate, Handle<Script> script,
1250 const UnoptimizedCompileFlags& flags,
1251 const UnoptimizedCompileState* compile_state,
1252 const FinalizeUnoptimizedCompilationDataList&
1253 finalize_unoptimized_compilation_data_list) {
1254 FinalizeUnoptimizedCompilation(isolate, script, flags, compile_state,
1255 finalize_unoptimized_compilation_data_list);
1256
1257 script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
1258
1259 UnoptimizedCompileState::ParallelTasks* parallel_tasks =
1260 compile_state->parallel_tasks();
1261 if (parallel_tasks) {
1262 CompilerDispatcher* dispatcher = parallel_tasks->dispatcher();
1263 for (auto& it : *parallel_tasks) {
1264 FunctionLiteral* literal = it.first;
1265 CompilerDispatcher::JobId job_id = it.second;
1266 MaybeHandle<SharedFunctionInfo> maybe_shared_for_task =
1267 script->FindSharedFunctionInfo(isolate,
1268 literal->function_literal_id());
1269 Handle<SharedFunctionInfo> shared_for_task;
1270 if (maybe_shared_for_task.ToHandle(&shared_for_task)) {
1271 dispatcher->RegisterSharedFunctionInfo(job_id, *shared_for_task);
1272 } else {
1273 dispatcher->AbortJob(job_id);
1274 }
1275 }
1276 }
1277
1278 if (isolate->NeedsSourcePositionsForProfiling()) {
1279 Script::InitLineEnds(isolate, script);
1280 }
1281 }
1282
1283 // Create shared function info for top level and shared function infos array for
1284 // inner functions.
1285 template <typename LocalIsolate>
CreateTopLevelSharedFunctionInfo(ParseInfo * parse_info,Handle<Script> script,LocalIsolate * isolate)1286 Handle<SharedFunctionInfo> CreateTopLevelSharedFunctionInfo(
1287 ParseInfo* parse_info, Handle<Script> script, LocalIsolate* isolate) {
1288 EnsureSharedFunctionInfosArrayOnScript(script, parse_info, isolate);
1289 DCHECK_EQ(kNoSourcePosition,
1290 parse_info->literal()->function_token_position());
1291 return isolate->factory()->NewSharedFunctionInfoForLiteral(
1292 parse_info->literal(), script, true);
1293 }
1294
CompileToplevel(ParseInfo * parse_info,Handle<Script> script,MaybeHandle<ScopeInfo> maybe_outer_scope_info,Isolate * isolate,IsCompiledScope * is_compiled_scope)1295 MaybeHandle<SharedFunctionInfo> CompileToplevel(
1296 ParseInfo* parse_info, Handle<Script> script,
1297 MaybeHandle<ScopeInfo> maybe_outer_scope_info, Isolate* isolate,
1298 IsCompiledScope* is_compiled_scope) {
1299 TimerEventScope<TimerEventCompileCode> top_level_timer(isolate);
1300 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileCode");
1301 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
1302
1303 PostponeInterruptsScope postpone(isolate);
1304 DCHECK(!isolate->native_context().is_null());
1305 RuntimeCallTimerScope runtimeTimer(
1306 isolate, parse_info->flags().is_eval()
1307 ? RuntimeCallCounterId::kCompileEval
1308 : RuntimeCallCounterId::kCompileScript);
1309 VMState<BYTECODE_COMPILER> state(isolate);
1310 if (parse_info->literal() == nullptr &&
1311 !parsing::ParseProgram(parse_info, script, maybe_outer_scope_info,
1312 isolate, parsing::ReportStatisticsMode::kYes)) {
1313 FailWithPendingException(isolate, script, parse_info,
1314 Compiler::ClearExceptionFlag::KEEP_EXCEPTION);
1315 return MaybeHandle<SharedFunctionInfo>();
1316 }
1317 // Measure how long it takes to do the compilation; only take the
1318 // rest of the function into account to avoid overlap with the
1319 // parsing statistics.
1320 HistogramTimer* rate = parse_info->flags().is_eval()
1321 ? isolate->counters()->compile_eval()
1322 : isolate->counters()->compile();
1323 HistogramTimerScope timer(rate);
1324 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1325 parse_info->flags().is_eval() ? "V8.CompileEval" : "V8.Compile");
1326
1327 // Prepare and execute compilation of the outer-most function.
1328
1329 // Create the SharedFunctionInfo and add it to the script's list.
1330 Handle<SharedFunctionInfo> shared_info =
1331 CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
1332
1333 FinalizeUnoptimizedCompilationDataList
1334 finalize_unoptimized_compilation_data_list;
1335
1336 if (!IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
1337 isolate, shared_info, script, parse_info, isolate->allocator(),
1338 is_compiled_scope, &finalize_unoptimized_compilation_data_list,
1339 nullptr)) {
1340 FailWithPendingException(isolate, script, parse_info,
1341 Compiler::ClearExceptionFlag::KEEP_EXCEPTION);
1342 return MaybeHandle<SharedFunctionInfo>();
1343 }
1344
1345 // Character stream shouldn't be used again.
1346 parse_info->ResetCharacterStream();
1347
1348 FinalizeUnoptimizedScriptCompilation(
1349 isolate, script, parse_info->flags(), parse_info->state(),
1350 finalize_unoptimized_compilation_data_list);
1351 return shared_info;
1352 }
1353
RuntimeCallCounterIdForCompileBackground(ParseInfo * parse_info)1354 RuntimeCallCounterId RuntimeCallCounterIdForCompileBackground(
1355 ParseInfo* parse_info) {
1356 if (parse_info->flags().is_toplevel()) {
1357 if (parse_info->flags().is_eval()) {
1358 return RuntimeCallCounterId::kCompileBackgroundEval;
1359 }
1360 return RuntimeCallCounterId::kCompileBackgroundScript;
1361 }
1362 return RuntimeCallCounterId::kCompileBackgroundFunction;
1363 }
1364
CompileAndFinalizeOnBackgroundThread(ParseInfo * parse_info,AccountingAllocator * allocator,Handle<Script> script,LocalIsolate * isolate,FinalizeUnoptimizedCompilationDataList * finalize_unoptimized_compilation_data_list,DeferredFinalizationJobDataList * jobs_to_retry_finalization_on_main_thread,IsCompiledScope * is_compiled_scope)1365 MaybeHandle<SharedFunctionInfo> CompileAndFinalizeOnBackgroundThread(
1366 ParseInfo* parse_info, AccountingAllocator* allocator,
1367 Handle<Script> script, LocalIsolate* isolate,
1368 FinalizeUnoptimizedCompilationDataList*
1369 finalize_unoptimized_compilation_data_list,
1370 DeferredFinalizationJobDataList* jobs_to_retry_finalization_on_main_thread,
1371 IsCompiledScope* is_compiled_scope) {
1372 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1373 "V8.CompileCodeBackground");
1374 RuntimeCallTimerScope runtimeTimer(
1375 parse_info->runtime_call_stats(),
1376 RuntimeCallCounterIdForCompileBackground(parse_info));
1377
1378 Handle<SharedFunctionInfo> shared_info =
1379 CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
1380
1381 if (!IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
1382 isolate, shared_info, script, parse_info, allocator,
1383 is_compiled_scope, finalize_unoptimized_compilation_data_list,
1384 jobs_to_retry_finalization_on_main_thread)) {
1385 return kNullMaybeHandle;
1386 }
1387
1388 // Character stream shouldn't be used again.
1389 parse_info->ResetCharacterStream();
1390
1391 return shared_info;
1392 }
1393
1394 // TODO(leszeks): Remove this once off-thread finalization is always on.
CompileOnBackgroundThread(ParseInfo * parse_info,AccountingAllocator * allocator,UnoptimizedCompilationJobList * jobs)1395 void CompileOnBackgroundThread(ParseInfo* parse_info,
1396 AccountingAllocator* allocator,
1397 UnoptimizedCompilationJobList* jobs) {
1398 DisallowHeapAccess no_heap_access;
1399 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1400 "V8.CompileCodeBackground");
1401 RuntimeCallTimerScope runtimeTimer(
1402 parse_info->runtime_call_stats(),
1403 RuntimeCallCounterIdForCompileBackground(parse_info));
1404
1405 // Generate the unoptimized bytecode or asm-js data.
1406 DCHECK(jobs->empty());
1407
1408 bool success = RecursivelyExecuteUnoptimizedCompilationJobs(
1409 parse_info, parse_info->literal(), allocator, jobs);
1410
1411 USE(success);
1412 DCHECK_EQ(success, !jobs->empty());
1413
1414 // Character stream shouldn't be used again.
1415 parse_info->ResetCharacterStream();
1416 }
1417
CompileToplevel(ParseInfo * parse_info,Handle<Script> script,Isolate * isolate,IsCompiledScope * is_compiled_scope)1418 MaybeHandle<SharedFunctionInfo> CompileToplevel(
1419 ParseInfo* parse_info, Handle<Script> script, Isolate* isolate,
1420 IsCompiledScope* is_compiled_scope) {
1421 return CompileToplevel(parse_info, script, kNullMaybeHandle, isolate,
1422 is_compiled_scope);
1423 }
1424
1425 } // namespace
1426
~CompilationHandleScope()1427 CompilationHandleScope::~CompilationHandleScope() {
1428 info_->set_persistent_handles(persistent_.Detach());
1429 }
1430
FinalizeUnoptimizedCompilationData(LocalIsolate * isolate,Handle<SharedFunctionInfo> function_handle,MaybeHandle<CoverageInfo> coverage_info,base::TimeDelta time_taken_to_execute,base::TimeDelta time_taken_to_finalize)1431 FinalizeUnoptimizedCompilationData::FinalizeUnoptimizedCompilationData(
1432 LocalIsolate* isolate, Handle<SharedFunctionInfo> function_handle,
1433 MaybeHandle<CoverageInfo> coverage_info,
1434 base::TimeDelta time_taken_to_execute,
1435 base::TimeDelta time_taken_to_finalize)
1436 : time_taken_to_execute_(time_taken_to_execute),
1437 time_taken_to_finalize_(time_taken_to_finalize),
1438 function_handle_(isolate->heap()->NewPersistentHandle(function_handle)),
1439 coverage_info_(isolate->heap()->NewPersistentMaybeHandle(coverage_info)) {
1440 }
1441
DeferredFinalizationJobData(LocalIsolate * isolate,Handle<SharedFunctionInfo> function_handle,std::unique_ptr<UnoptimizedCompilationJob> job)1442 DeferredFinalizationJobData::DeferredFinalizationJobData(
1443 LocalIsolate* isolate, Handle<SharedFunctionInfo> function_handle,
1444 std::unique_ptr<UnoptimizedCompilationJob> job)
1445 : function_handle_(isolate->heap()->NewPersistentHandle(function_handle)),
1446 job_(std::move(job)) {}
1447
BackgroundCompileTask(ScriptStreamingData * streamed_data,Isolate * isolate)1448 BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* streamed_data,
1449 Isolate* isolate)
1450 : flags_(UnoptimizedCompileFlags::ForToplevelCompile(
1451 isolate, true, construct_language_mode(FLAG_use_strict),
1452 REPLMode::kNo)),
1453 compile_state_(isolate),
1454 info_(std::make_unique<ParseInfo>(isolate, flags_, &compile_state_)),
1455 isolate_for_local_isolate_(isolate),
1456 start_position_(0),
1457 end_position_(0),
1458 function_literal_id_(kFunctionLiteralIdTopLevel),
1459 stack_size_(i::FLAG_stack_size),
1460 worker_thread_runtime_call_stats_(
1461 isolate->counters()->worker_thread_runtime_call_stats()),
1462 timer_(isolate->counters()->compile_script_on_background()),
1463 language_mode_(info_->language_mode()) {
1464 VMState<PARSER> state(isolate);
1465
1466 // Prepare the data for the internalization phase and compilation phase, which
1467 // will happen in the main thread after parsing.
1468
1469 LOG(isolate, ScriptEvent(Logger::ScriptEventType::kStreamingCompile,
1470 info_->flags().script_id()));
1471
1472 std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
1473 streamed_data->source_stream.get(), streamed_data->encoding));
1474 info_->set_character_stream(std::move(stream));
1475 }
1476
BackgroundCompileTask(const ParseInfo * outer_parse_info,const AstRawString * function_name,const FunctionLiteral * function_literal,WorkerThreadRuntimeCallStats * worker_thread_runtime_stats,TimedHistogram * timer,int max_stack_size)1477 BackgroundCompileTask::BackgroundCompileTask(
1478 const ParseInfo* outer_parse_info, const AstRawString* function_name,
1479 const FunctionLiteral* function_literal,
1480 WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
1481 TimedHistogram* timer, int max_stack_size)
1482 : flags_(UnoptimizedCompileFlags::ForToplevelFunction(
1483 outer_parse_info->flags(), function_literal)),
1484 compile_state_(*outer_parse_info->state()),
1485 info_(ParseInfo::ForToplevelFunction(flags_, &compile_state_,
1486 function_literal, function_name)),
1487 isolate_for_local_isolate_(nullptr),
1488 start_position_(function_literal->start_position()),
1489 end_position_(function_literal->end_position()),
1490 function_literal_id_(function_literal->function_literal_id()),
1491 stack_size_(max_stack_size),
1492 worker_thread_runtime_call_stats_(worker_thread_runtime_stats),
1493 timer_(timer),
1494 language_mode_(info_->language_mode()) {
1495 DCHECK_EQ(outer_parse_info->parameters_end_pos(), kNoSourcePosition);
1496 DCHECK_NULL(outer_parse_info->extension());
1497
1498 DCHECK(!function_literal->is_toplevel());
1499
1500 // Clone the character stream so both can be accessed independently.
1501 std::unique_ptr<Utf16CharacterStream> character_stream =
1502 outer_parse_info->character_stream()->Clone();
1503 character_stream->Seek(start_position_);
1504 info_->set_character_stream(std::move(character_stream));
1505
1506 // Get preparsed scope data from the function literal.
1507 if (function_literal->produced_preparse_data()) {
1508 ZonePreparseData* serialized_data =
1509 function_literal->produced_preparse_data()->Serialize(info_->zone());
1510 info_->set_consumed_preparse_data(
1511 ConsumedPreparseData::For(info_->zone(), serialized_data));
1512 }
1513 }
1514
1515 BackgroundCompileTask::~BackgroundCompileTask() = default;
1516
1517 namespace {
1518
1519 // A scope object that ensures a parse info's runtime call stats and stack limit
1520 // are set correctly during worker-thread compile, and restores it after going
1521 // out of scope.
1522 class OffThreadParseInfoScope {
1523 public:
OffThreadParseInfoScope(ParseInfo * parse_info,WorkerThreadRuntimeCallStats * worker_thread_runtime_stats,int stack_size)1524 OffThreadParseInfoScope(
1525 ParseInfo* parse_info,
1526 WorkerThreadRuntimeCallStats* worker_thread_runtime_stats, int stack_size)
1527 : parse_info_(parse_info),
1528 original_runtime_call_stats_(parse_info_->runtime_call_stats()),
1529 original_stack_limit_(parse_info_->stack_limit()),
1530 worker_thread_scope_(worker_thread_runtime_stats) {
1531 parse_info_->SetPerThreadState(GetCurrentStackPosition() - stack_size * KB,
1532 worker_thread_scope_.Get());
1533 }
1534
~OffThreadParseInfoScope()1535 ~OffThreadParseInfoScope() {
1536 DCHECK_NOT_NULL(parse_info_);
1537 parse_info_->SetPerThreadState(original_stack_limit_,
1538 original_runtime_call_stats_);
1539 }
1540
1541 private:
1542 ParseInfo* parse_info_;
1543 RuntimeCallStats* original_runtime_call_stats_;
1544 uintptr_t original_stack_limit_;
1545 WorkerThreadRuntimeCallStatsScope worker_thread_scope_;
1546
1547 DISALLOW_COPY_AND_ASSIGN(OffThreadParseInfoScope);
1548 };
1549
1550 } // namespace
1551
Run()1552 void BackgroundCompileTask::Run() {
1553 TimedHistogramScope timer(timer_);
1554 base::Optional<OffThreadParseInfoScope> off_thread_scope(
1555 base::in_place, info_.get(), worker_thread_runtime_call_stats_,
1556 stack_size_);
1557 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1558 "BackgroundCompileTask::Run");
1559 RuntimeCallTimerScope runtimeTimer(
1560 info_->runtime_call_stats(),
1561 RuntimeCallCounterId::kCompileBackgroundCompileTask);
1562
1563 // Update the character stream's runtime call stats.
1564 info_->character_stream()->set_runtime_call_stats(
1565 info_->runtime_call_stats());
1566
1567 // Parser needs to stay alive for finalizing the parsing on the main
1568 // thread.
1569 parser_.reset(new Parser(info_.get()));
1570 parser_->InitializeEmptyScopeChain(info_.get());
1571
1572 parser_->ParseOnBackground(info_.get(), start_position_, end_position_,
1573 function_literal_id_);
1574
1575 // Save the language mode.
1576 language_mode_ = info_->language_mode();
1577
1578 if (!FLAG_finalize_streaming_on_background) {
1579 if (info_->literal() != nullptr) {
1580 CompileOnBackgroundThread(info_.get(), compile_state_.allocator(),
1581 &compilation_jobs_);
1582 }
1583 } else {
1584 DCHECK(info_->flags().is_toplevel());
1585
1586 LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground);
1587 UnparkedScope unparked_scope(isolate.heap());
1588 LocalHandleScope handle_scope(&isolate);
1589
1590 info_->ast_value_factory()->Internalize(&isolate);
1591
1592 // We don't have the script source, origin, or details yet, so use default
1593 // values for them. These will be fixed up during the main-thread merge.
1594 Handle<Script> script =
1595 info_->CreateScript(&isolate, isolate.factory()->empty_string(),
1596 kNullMaybeHandle, ScriptOriginOptions());
1597
1598 parser_->HandleSourceURLComments(&isolate, script);
1599
1600 MaybeHandle<SharedFunctionInfo> maybe_result;
1601 if (info_->literal() != nullptr) {
1602 maybe_result = CompileAndFinalizeOnBackgroundThread(
1603 info_.get(), compile_state_.allocator(), script, &isolate,
1604 &finalize_unoptimized_compilation_data_,
1605 &jobs_to_retry_finalization_on_main_thread_, &is_compiled_scope_);
1606 } else {
1607 DCHECK(compile_state_.pending_error_handler()->has_pending_error());
1608 PreparePendingException(&isolate, info_.get());
1609 }
1610
1611 outer_function_sfi_ =
1612 isolate.heap()->NewPersistentMaybeHandle(maybe_result);
1613 script_ = isolate.heap()->NewPersistentHandle(script);
1614
1615 persistent_handles_ = isolate.heap()->DetachPersistentHandles();
1616
1617 {
1618 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1619 "V8.FinalizeCodeBackground.ReleaseParser");
1620 DCHECK_EQ(language_mode_, info_->language_mode());
1621 off_thread_scope.reset();
1622 parser_.reset();
1623 info_.reset();
1624 }
1625 }
1626 }
1627
GetOuterFunctionSfi(Isolate * isolate)1628 MaybeHandle<SharedFunctionInfo> BackgroundCompileTask::GetOuterFunctionSfi(
1629 Isolate* isolate) {
1630 // outer_function_sfi_ is a persistent Handle, tied to the lifetime of the
1631 // persistent_handles_ member, so create a new Handle to let it outlive
1632 // the BackgroundCompileTask.
1633 Handle<SharedFunctionInfo> result;
1634 if (outer_function_sfi_.ToHandle(&result)) {
1635 return handle(*result, isolate);
1636 }
1637 return kNullMaybeHandle;
1638 }
1639
GetScript(Isolate * isolate)1640 Handle<Script> BackgroundCompileTask::GetScript(Isolate* isolate) {
1641 // script_ is a persistent Handle, tied to the lifetime of the
1642 // persistent_handles_ member, so create a new Handle to let it outlive
1643 // the BackgroundCompileTask.
1644 return handle(*script_, isolate);
1645 }
1646
1647 // ----------------------------------------------------------------------------
1648 // Implementation of Compiler
1649
1650 // static
CollectSourcePositions(Isolate * isolate,Handle<SharedFunctionInfo> shared_info)1651 bool Compiler::CollectSourcePositions(Isolate* isolate,
1652 Handle<SharedFunctionInfo> shared_info) {
1653 DCHECK(shared_info->is_compiled());
1654 DCHECK(shared_info->HasBytecodeArray());
1655 DCHECK(!shared_info->GetBytecodeArray().HasSourcePositionTable());
1656
1657 // Source position collection should be context independent.
1658 NullContextScope null_context_scope(isolate);
1659
1660 // Collecting source positions requires allocating a new source position
1661 // table.
1662 DCHECK(AllowHeapAllocation::IsAllowed());
1663 DCHECK(AllowGarbageCollection::IsAllowed());
1664
1665 Handle<BytecodeArray> bytecode =
1666 handle(shared_info->GetBytecodeArray(), isolate);
1667
1668 // TODO(v8:8510): Push the CLEAR_EXCEPTION flag or something like it down into
1669 // the parser so it aborts without setting a pending exception, which then
1670 // gets thrown. This would avoid the situation where potentially we'd reparse
1671 // several times (running out of stack each time) before hitting this limit.
1672 if (GetCurrentStackPosition() < isolate->stack_guard()->real_climit()) {
1673 // Stack is already exhausted.
1674 bytecode->SetSourcePositionsFailedToCollect();
1675 return false;
1676 }
1677
1678 DCHECK(AllowCompilation::IsAllowed(isolate));
1679 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
1680 DCHECK(!isolate->has_pending_exception());
1681 VMState<BYTECODE_COMPILER> state(isolate);
1682 PostponeInterruptsScope postpone(isolate);
1683 RuntimeCallTimerScope runtimeTimer(
1684 isolate, RuntimeCallCounterId::kCompileCollectSourcePositions);
1685 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1686 "V8.CollectSourcePositions");
1687 HistogramTimerScope timer(isolate->counters()->collect_source_positions());
1688
1689 // Set up parse info.
1690 UnoptimizedCompileFlags flags =
1691 UnoptimizedCompileFlags::ForFunctionCompile(isolate, *shared_info);
1692 flags.set_is_lazy_compile(true);
1693 flags.set_collect_source_positions(true);
1694 flags.set_allow_natives_syntax(FLAG_allow_natives_syntax);
1695
1696 UnoptimizedCompileState compile_state(isolate);
1697 ParseInfo parse_info(isolate, flags, &compile_state);
1698
1699 // Parse and update ParseInfo with the results. Don't update parsing
1700 // statistics since we've already parsed the code before.
1701 if (!parsing::ParseAny(&parse_info, shared_info, isolate,
1702 parsing::ReportStatisticsMode::kNo)) {
1703 // Parsing failed probably as a result of stack exhaustion.
1704 bytecode->SetSourcePositionsFailedToCollect();
1705 return FailAndClearPendingException(isolate);
1706 }
1707
1708 // Character stream shouldn't be used again.
1709 parse_info.ResetCharacterStream();
1710
1711 // Generate the unoptimized bytecode.
1712 // TODO(v8:8510): Consider forcing preparsing of inner functions to avoid
1713 // wasting time fully parsing them when they won't ever be used.
1714 std::unique_ptr<UnoptimizedCompilationJob> job;
1715 {
1716 job = interpreter::Interpreter::NewSourcePositionCollectionJob(
1717 &parse_info, parse_info.literal(), bytecode, isolate->allocator());
1718
1719 if (!job || job->ExecuteJob() != CompilationJob::SUCCEEDED ||
1720 job->FinalizeJob(shared_info, isolate) != CompilationJob::SUCCEEDED) {
1721 // Recompiling failed probably as a result of stack exhaustion.
1722 bytecode->SetSourcePositionsFailedToCollect();
1723 return FailAndClearPendingException(isolate);
1724 }
1725 }
1726
1727 DCHECK(job->compilation_info()->flags().collect_source_positions());
1728
1729 // If debugging, make sure that instrumented bytecode has the source position
1730 // table set on it as well.
1731 if (shared_info->HasDebugInfo() &&
1732 shared_info->GetDebugInfo().HasInstrumentedBytecodeArray()) {
1733 ByteArray source_position_table =
1734 job->compilation_info()->bytecode_array()->SourcePositionTable();
1735 shared_info->GetDebugBytecodeArray().set_source_position_table(
1736 source_position_table, kReleaseStore);
1737 }
1738
1739 DCHECK(!isolate->has_pending_exception());
1740 DCHECK(shared_info->is_compiled_scope(isolate).is_compiled());
1741 return true;
1742 }
1743
1744 // static
Compile(Handle<SharedFunctionInfo> shared_info,ClearExceptionFlag flag,IsCompiledScope * is_compiled_scope)1745 bool Compiler::Compile(Handle<SharedFunctionInfo> shared_info,
1746 ClearExceptionFlag flag,
1747 IsCompiledScope* is_compiled_scope) {
1748 // We should never reach here if the function is already compiled.
1749 DCHECK(!shared_info->is_compiled());
1750 DCHECK(!is_compiled_scope->is_compiled());
1751
1752 Isolate* isolate = shared_info->GetIsolate();
1753 DCHECK(AllowCompilation::IsAllowed(isolate));
1754 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
1755 DCHECK(!isolate->has_pending_exception());
1756 DCHECK(!shared_info->HasBytecodeArray());
1757 VMState<BYTECODE_COMPILER> state(isolate);
1758 PostponeInterruptsScope postpone(isolate);
1759 TimerEventScope<TimerEventCompileCode> compile_timer(isolate);
1760 RuntimeCallTimerScope runtimeTimer(isolate,
1761 RuntimeCallCounterId::kCompileFunction);
1762 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileCode");
1763 AggregatedHistogramTimerScope timer(isolate->counters()->compile_lazy());
1764
1765 Handle<Script> script(Script::cast(shared_info->script()), isolate);
1766
1767 // Set up parse info.
1768 UnoptimizedCompileFlags flags =
1769 UnoptimizedCompileFlags::ForFunctionCompile(isolate, *shared_info);
1770 flags.set_is_lazy_compile(true);
1771
1772 UnoptimizedCompileState compile_state(isolate);
1773 ParseInfo parse_info(isolate, flags, &compile_state);
1774
1775 // Check if the compiler dispatcher has shared_info enqueued for compile.
1776 CompilerDispatcher* dispatcher = isolate->compiler_dispatcher();
1777 if (dispatcher->IsEnqueued(shared_info)) {
1778 if (!dispatcher->FinishNow(shared_info)) {
1779 return FailWithPendingException(isolate, script, &parse_info, flag);
1780 }
1781 *is_compiled_scope = shared_info->is_compiled_scope(isolate);
1782 DCHECK(is_compiled_scope->is_compiled());
1783 return true;
1784 }
1785
1786 if (shared_info->HasUncompiledDataWithPreparseData()) {
1787 parse_info.set_consumed_preparse_data(ConsumedPreparseData::For(
1788 isolate,
1789 handle(
1790 shared_info->uncompiled_data_with_preparse_data().preparse_data(),
1791 isolate)));
1792 }
1793
1794 // Parse and update ParseInfo with the results.
1795 if (!parsing::ParseAny(&parse_info, shared_info, isolate,
1796 parsing::ReportStatisticsMode::kYes)) {
1797 return FailWithPendingException(isolate, script, &parse_info, flag);
1798 }
1799
1800 // Generate the unoptimized bytecode or asm-js data.
1801 FinalizeUnoptimizedCompilationDataList
1802 finalize_unoptimized_compilation_data_list;
1803
1804 if (!IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
1805 isolate, shared_info, script, &parse_info, isolate->allocator(),
1806 is_compiled_scope, &finalize_unoptimized_compilation_data_list,
1807 nullptr)) {
1808 return FailWithPendingException(isolate, script, &parse_info, flag);
1809 }
1810
1811 FinalizeUnoptimizedCompilation(isolate, script, flags, &compile_state,
1812 finalize_unoptimized_compilation_data_list);
1813
1814 DCHECK(!isolate->has_pending_exception());
1815 DCHECK(is_compiled_scope->is_compiled());
1816 return true;
1817 }
1818
1819 // static
Compile(Handle<JSFunction> function,ClearExceptionFlag flag,IsCompiledScope * is_compiled_scope)1820 bool Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag flag,
1821 IsCompiledScope* is_compiled_scope) {
1822 // We should never reach here if the function is already compiled or
1823 // optimized.
1824 DCHECK(!function->is_compiled());
1825 DCHECK(!function->HasOptimizationMarker());
1826 DCHECK(!function->HasAvailableOptimizedCode());
1827
1828 // Reset the JSFunction if we are recompiling due to the bytecode having been
1829 // flushed.
1830 function->ResetIfBytecodeFlushed();
1831
1832 Isolate* isolate = function->GetIsolate();
1833 Handle<SharedFunctionInfo> shared_info = handle(function->shared(), isolate);
1834
1835 // Ensure shared function info is compiled.
1836 *is_compiled_scope = shared_info->is_compiled_scope(isolate);
1837 if (!is_compiled_scope->is_compiled() &&
1838 !Compile(shared_info, flag, is_compiled_scope)) {
1839 return false;
1840 }
1841 DCHECK(is_compiled_scope->is_compiled());
1842 Handle<Code> code = handle(shared_info->GetCode(), isolate);
1843
1844 // Initialize the feedback cell for this JSFunction.
1845 JSFunction::InitializeFeedbackCell(function, is_compiled_scope);
1846
1847 // Optimize now if --always-opt is enabled.
1848 if (FLAG_always_opt && !function->shared().HasAsmWasmData()) {
1849 CompilerTracer::TraceOptimizeForAlwaysOpt(isolate, function,
1850 CodeKindForTopTier());
1851
1852 Handle<Code> maybe_code;
1853 if (GetOptimizedCode(function, ConcurrencyMode::kNotConcurrent,
1854 CodeKindForTopTier())
1855 .ToHandle(&maybe_code)) {
1856 code = maybe_code;
1857 }
1858 }
1859
1860 // Install code on closure.
1861 function->set_code(*code);
1862
1863 // Check postconditions on success.
1864 DCHECK(!isolate->has_pending_exception());
1865 DCHECK(function->shared().is_compiled());
1866 DCHECK(function->is_compiled());
1867 return true;
1868 }
1869
1870 // static
FinalizeBackgroundCompileTask(BackgroundCompileTask * task,Handle<SharedFunctionInfo> shared_info,Isolate * isolate,ClearExceptionFlag flag)1871 bool Compiler::FinalizeBackgroundCompileTask(
1872 BackgroundCompileTask* task, Handle<SharedFunctionInfo> shared_info,
1873 Isolate* isolate, ClearExceptionFlag flag) {
1874 DCHECK(!FLAG_finalize_streaming_on_background);
1875
1876 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
1877 "V8.FinalizeBackgroundCompileTask");
1878 RuntimeCallTimerScope runtimeTimer(
1879 isolate, RuntimeCallCounterId::kCompileFinalizeBackgroundCompileTask);
1880 HandleScope scope(isolate);
1881 ParseInfo* parse_info = task->info();
1882 DCHECK(!parse_info->flags().is_toplevel());
1883 DCHECK(!shared_info->is_compiled());
1884
1885 Handle<Script> script(Script::cast(shared_info->script()), isolate);
1886 parse_info->CheckFlagsForFunctionFromScript(*script);
1887
1888 task->parser()->UpdateStatistics(isolate, script);
1889 task->parser()->HandleSourceURLComments(isolate, script);
1890
1891 if (task->compilation_jobs()->empty()) {
1892 // Parsing or compile failed on background thread - report error messages.
1893 return FailWithPendingException(isolate, script, parse_info, flag);
1894 }
1895
1896 // Parsing has succeeded - finalize compilation.
1897 parse_info->ast_value_factory()->Internalize(isolate);
1898 if (!FinalizeAllUnoptimizedCompilationJobs(
1899 parse_info, isolate, script, task->compilation_jobs(),
1900 task->finalize_unoptimized_compilation_data())) {
1901 // Finalization failed - throw an exception.
1902 return FailWithPendingException(isolate, script, parse_info, flag);
1903 }
1904 FinalizeUnoptimizedCompilation(
1905 isolate, script, parse_info->flags(), parse_info->state(),
1906 *task->finalize_unoptimized_compilation_data());
1907
1908 DCHECK(!isolate->has_pending_exception());
1909 DCHECK(shared_info->is_compiled());
1910 return true;
1911 }
1912
1913 // static
CompileOptimized(Handle<JSFunction> function,ConcurrencyMode mode,CodeKind code_kind)1914 bool Compiler::CompileOptimized(Handle<JSFunction> function,
1915 ConcurrencyMode mode, CodeKind code_kind) {
1916 DCHECK(CodeKindIsOptimizedJSFunction(code_kind));
1917
1918 // If the requested code kind is already available, do nothing.
1919 if (function->HasAvailableCodeKind(code_kind)) return true;
1920
1921 Isolate* isolate = function->GetIsolate();
1922 DCHECK(AllowCompilation::IsAllowed(isolate));
1923
1924 Handle<Code> code;
1925 if (!GetOptimizedCode(function, mode, code_kind).ToHandle(&code)) {
1926 // Optimization failed, get unoptimized code. Unoptimized code must exist
1927 // already if we are optimizing.
1928 DCHECK(!isolate->has_pending_exception());
1929 DCHECK(function->shared().is_compiled());
1930 DCHECK(function->shared().IsInterpreted());
1931 code = BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
1932 }
1933
1934 if (!IsForNativeContextIndependentCachingOnly(code_kind)) {
1935 function->set_code(*code);
1936 }
1937
1938 // Check postconditions on success.
1939 DCHECK(!isolate->has_pending_exception());
1940 DCHECK(function->shared().is_compiled());
1941 DCHECK(function->is_compiled());
1942 if (UsesOptimizationMarker(code_kind)) {
1943 DCHECK_IMPLIES(function->HasOptimizationMarker(),
1944 function->IsInOptimizationQueue());
1945 DCHECK_IMPLIES(function->HasOptimizationMarker(),
1946 function->ChecksOptimizationMarker());
1947 DCHECK_IMPLIES(function->IsInOptimizationQueue(),
1948 mode == ConcurrencyMode::kConcurrent);
1949 }
1950 return true;
1951 }
1952
1953 // static
CompileForLiveEdit(ParseInfo * parse_info,Handle<Script> script,Isolate * isolate)1954 MaybeHandle<SharedFunctionInfo> Compiler::CompileForLiveEdit(
1955 ParseInfo* parse_info, Handle<Script> script, Isolate* isolate) {
1956 IsCompiledScope is_compiled_scope;
1957 return CompileToplevel(parse_info, script, isolate, &is_compiled_scope);
1958 }
1959
1960 // static
GetFunctionFromEval(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,LanguageMode language_mode,ParseRestriction restriction,int parameters_end_pos,int eval_scope_position,int eval_position)1961 MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
1962 Handle<String> source, Handle<SharedFunctionInfo> outer_info,
1963 Handle<Context> context, LanguageMode language_mode,
1964 ParseRestriction restriction, int parameters_end_pos,
1965 int eval_scope_position, int eval_position) {
1966 Isolate* isolate = context->GetIsolate();
1967 int source_length = source->length();
1968 isolate->counters()->total_eval_size()->Increment(source_length);
1969 isolate->counters()->total_compile_size()->Increment(source_length);
1970
1971 // The cache lookup key needs to be aware of the separation between the
1972 // parameters and the body to prevent this valid invocation:
1973 // Function("", "function anonymous(\n/**/) {\n}");
1974 // from adding an entry that falsely approves this invalid invocation:
1975 // Function("\n/**/) {\nfunction anonymous(", "}");
1976 // The actual eval_scope_position for indirect eval and CreateDynamicFunction
1977 // is unused (just 0), which means it's an available field to use to indicate
1978 // this separation. But to make sure we're not causing other false hits, we
1979 // negate the scope position.
1980 if (restriction == ONLY_SINGLE_FUNCTION_LITERAL &&
1981 parameters_end_pos != kNoSourcePosition) {
1982 // use the parameters_end_pos as the eval_scope_position in the eval cache.
1983 DCHECK_EQ(eval_scope_position, 0);
1984 eval_scope_position = -parameters_end_pos;
1985 }
1986 CompilationCache* compilation_cache = isolate->compilation_cache();
1987 InfoCellPair eval_result = compilation_cache->LookupEval(
1988 source, outer_info, context, language_mode, eval_scope_position);
1989 Handle<FeedbackCell> feedback_cell;
1990 if (eval_result.has_feedback_cell()) {
1991 feedback_cell = handle(eval_result.feedback_cell(), isolate);
1992 }
1993
1994 Handle<SharedFunctionInfo> shared_info;
1995 Handle<Script> script;
1996 IsCompiledScope is_compiled_scope;
1997 bool allow_eval_cache;
1998 if (eval_result.has_shared()) {
1999 shared_info = Handle<SharedFunctionInfo>(eval_result.shared(), isolate);
2000 script = Handle<Script>(Script::cast(shared_info->script()), isolate);
2001 is_compiled_scope = shared_info->is_compiled_scope(isolate);
2002 allow_eval_cache = true;
2003 } else {
2004 UnoptimizedCompileFlags flags = UnoptimizedCompileFlags::ForToplevelCompile(
2005 isolate, true, language_mode, REPLMode::kNo);
2006 flags.set_is_eval(true);
2007 flags.set_parse_restriction(restriction);
2008
2009 UnoptimizedCompileState compile_state(isolate);
2010 ParseInfo parse_info(isolate, flags, &compile_state);
2011 parse_info.set_parameters_end_pos(parameters_end_pos);
2012 DCHECK(!parse_info.flags().is_module());
2013
2014 MaybeHandle<ScopeInfo> maybe_outer_scope_info;
2015 if (!context->IsNativeContext()) {
2016 maybe_outer_scope_info = handle(context->scope_info(), isolate);
2017 }
2018 script =
2019 parse_info.CreateScript(isolate, source, kNullMaybeHandle,
2020 OriginOptionsForEval(outer_info->script()));
2021 script->set_eval_from_shared(*outer_info);
2022 if (eval_position == kNoSourcePosition) {
2023 // If the position is missing, attempt to get the code offset by
2024 // walking the stack. Do not translate the code offset into source
2025 // position, but store it as negative value for lazy translation.
2026 StackTraceFrameIterator it(isolate);
2027 if (!it.done() && it.is_javascript()) {
2028 FrameSummary summary = FrameSummary::GetTop(it.javascript_frame());
2029 script->set_eval_from_shared(
2030 summary.AsJavaScript().function()->shared());
2031 script->set_origin_options(OriginOptionsForEval(*summary.script()));
2032 eval_position = -summary.code_offset();
2033 } else {
2034 eval_position = 0;
2035 }
2036 }
2037 script->set_eval_from_position(eval_position);
2038
2039 if (!CompileToplevel(&parse_info, script, maybe_outer_scope_info, isolate,
2040 &is_compiled_scope)
2041 .ToHandle(&shared_info)) {
2042 return MaybeHandle<JSFunction>();
2043 }
2044 allow_eval_cache = parse_info.allow_eval_cache();
2045 }
2046
2047 // If caller is strict mode, the result must be in strict mode as well.
2048 DCHECK(is_sloppy(language_mode) || is_strict(shared_info->language_mode()));
2049
2050 Handle<JSFunction> result;
2051 if (eval_result.has_shared()) {
2052 if (eval_result.has_feedback_cell()) {
2053 result = isolate->factory()->NewFunctionFromSharedFunctionInfo(
2054 shared_info, context, feedback_cell, AllocationType::kYoung);
2055 } else {
2056 result = isolate->factory()->NewFunctionFromSharedFunctionInfo(
2057 shared_info, context, AllocationType::kYoung);
2058 JSFunction::InitializeFeedbackCell(result, &is_compiled_scope);
2059 if (allow_eval_cache) {
2060 // Make sure to cache this result.
2061 Handle<FeedbackCell> new_feedback_cell(result->raw_feedback_cell(),
2062 isolate);
2063 compilation_cache->PutEval(source, outer_info, context, shared_info,
2064 new_feedback_cell, eval_scope_position);
2065 }
2066 }
2067 } else {
2068 result = isolate->factory()->NewFunctionFromSharedFunctionInfo(
2069 shared_info, context, AllocationType::kYoung);
2070 JSFunction::InitializeFeedbackCell(result, &is_compiled_scope);
2071 if (allow_eval_cache) {
2072 // Add the SharedFunctionInfo and the LiteralsArray to the eval cache if
2073 // we didn't retrieve from there.
2074 Handle<FeedbackCell> new_feedback_cell(result->raw_feedback_cell(),
2075 isolate);
2076 compilation_cache->PutEval(source, outer_info, context, shared_info,
2077 new_feedback_cell, eval_scope_position);
2078 }
2079 }
2080 DCHECK(is_compiled_scope.is_compiled());
2081
2082 return result;
2083 }
2084
2085 // Check whether embedder allows code generation in this context.
2086 // (via v8::Isolate::SetAllowCodeGenerationFromStringsCallback)
CodeGenerationFromStringsAllowed(Isolate * isolate,Handle<Context> context,Handle<String> source)2087 bool CodeGenerationFromStringsAllowed(Isolate* isolate, Handle<Context> context,
2088 Handle<String> source) {
2089 DCHECK(context->allow_code_gen_from_strings().IsFalse(isolate));
2090 DCHECK(isolate->allow_code_gen_callback());
2091
2092 // Callback set. Let it decide if code generation is allowed.
2093 VMState<EXTERNAL> state(isolate);
2094 RuntimeCallTimerScope timer(
2095 isolate, RuntimeCallCounterId::kCodeGenerationFromStringsCallbacks);
2096 AllowCodeGenerationFromStringsCallback callback =
2097 isolate->allow_code_gen_callback();
2098 return callback(v8::Utils::ToLocal(context), v8::Utils::ToLocal(source));
2099 }
2100
2101 // Check whether embedder allows code generation in this context.
2102 // (via v8::Isolate::SetModifyCodeGenerationFromStringsCallback
2103 // or v8::Isolate::SetModifyCodeGenerationFromStringsCallback2)
ModifyCodeGenerationFromStrings(Isolate * isolate,Handle<Context> context,Handle<i::Object> * source,bool is_code_like)2104 bool ModifyCodeGenerationFromStrings(Isolate* isolate, Handle<Context> context,
2105 Handle<i::Object>* source,
2106 bool is_code_like) {
2107 DCHECK(isolate->modify_code_gen_callback() ||
2108 isolate->modify_code_gen_callback2());
2109 DCHECK(source);
2110
2111 // Callback set. Run it, and use the return value as source, or block
2112 // execution if it's not set.
2113 VMState<EXTERNAL> state(isolate);
2114 RuntimeCallTimerScope timer(
2115 isolate, RuntimeCallCounterId::kCodeGenerationFromStringsCallbacks);
2116 ModifyCodeGenerationFromStringsResult result =
2117 isolate->modify_code_gen_callback()
2118 ? isolate->modify_code_gen_callback()(v8::Utils::ToLocal(context),
2119 v8::Utils::ToLocal(*source))
2120 : isolate->modify_code_gen_callback2()(v8::Utils::ToLocal(context),
2121 v8::Utils::ToLocal(*source),
2122 is_code_like);
2123 if (result.codegen_allowed && !result.modified_source.IsEmpty()) {
2124 // Use the new source (which might be the same as the old source).
2125 *source =
2126 Utils::OpenHandle(*result.modified_source.ToLocalChecked(), false);
2127 }
2128 return result.codegen_allowed;
2129 }
2130
2131 // Run Embedder-mandated checks before generating code from a string.
2132 //
2133 // Returns a string to be used for compilation, or a flag that an object type
2134 // was encountered that is neither a string, nor something the embedder knows
2135 // how to handle.
2136 //
2137 // Returns: (assuming: std::tie(source, unknown_object))
2138 // - !source.is_null(): compilation allowed, source contains the source string.
2139 // - unknown_object is true: compilation allowed, but we don't know how to
2140 // deal with source_object.
2141 // - source.is_null() && !unknown_object: compilation should be blocked.
2142 //
2143 // - !source_is_null() and unknown_object can't be true at the same time.
2144
2145 // static
ValidateDynamicCompilationSource(Isolate * isolate,Handle<Context> context,Handle<i::Object> original_source,bool is_code_like)2146 std::pair<MaybeHandle<String>, bool> Compiler::ValidateDynamicCompilationSource(
2147 Isolate* isolate, Handle<Context> context,
2148 Handle<i::Object> original_source, bool is_code_like) {
2149 // Check if the context unconditionally allows code gen from strings.
2150 // allow_code_gen_from_strings can be many things, so we'll always check
2151 // against the 'false' literal, so that e.g. undefined and 'true' are treated
2152 // the same.
2153 if (!context->allow_code_gen_from_strings().IsFalse(isolate) &&
2154 original_source->IsString()) {
2155 return {Handle<String>::cast(original_source), false};
2156 }
2157
2158 // Check if the context allows code generation for this string.
2159 // allow_code_gen_callback only allows proper strings.
2160 // (I.e., let allow_code_gen_callback decide, if it has been set.)
2161 if (isolate->allow_code_gen_callback()) {
2162 // If we run into this condition, the embedder has marked some object
2163 // templates as "code like", but has given us a callback that only accepts
2164 // strings. That makes no sense.
2165 DCHECK(!original_source->IsCodeLike(isolate));
2166
2167 if (!original_source->IsString()) {
2168 return {MaybeHandle<String>(), true};
2169 }
2170 Handle<String> string_source = Handle<String>::cast(original_source);
2171 if (!CodeGenerationFromStringsAllowed(isolate, context, string_source)) {
2172 return {MaybeHandle<String>(), false};
2173 }
2174 return {string_source, false};
2175 }
2176
2177 // Check if the context wants to block or modify this source object.
2178 // Double-check that we really have a string now.
2179 // (Let modify_code_gen_callback decide, if it's been set.)
2180 if (isolate->modify_code_gen_callback() ||
2181 isolate->modify_code_gen_callback2()) {
2182 Handle<i::Object> modified_source = original_source;
2183 if (!ModifyCodeGenerationFromStrings(isolate, context, &modified_source,
2184 is_code_like)) {
2185 return {MaybeHandle<String>(), false};
2186 }
2187 if (!modified_source->IsString()) {
2188 return {MaybeHandle<String>(), true};
2189 }
2190 return {Handle<String>::cast(modified_source), false};
2191 }
2192
2193 if (!context->allow_code_gen_from_strings().IsFalse(isolate) &&
2194 original_source->IsCodeLike(isolate)) {
2195 // Codegen is unconditionally allowed, and we're been given a CodeLike
2196 // object. Stringify.
2197 MaybeHandle<String> stringified_source =
2198 Object::ToString(isolate, original_source);
2199 return {stringified_source, stringified_source.is_null()};
2200 }
2201
2202 // If unconditional codegen was disabled, and no callback defined, we block
2203 // strings and allow all other objects.
2204 return {MaybeHandle<String>(), !original_source->IsString()};
2205 }
2206
2207 // static
GetFunctionFromValidatedString(Handle<Context> context,MaybeHandle<String> source,ParseRestriction restriction,int parameters_end_pos)2208 MaybeHandle<JSFunction> Compiler::GetFunctionFromValidatedString(
2209 Handle<Context> context, MaybeHandle<String> source,
2210 ParseRestriction restriction, int parameters_end_pos) {
2211 Isolate* const isolate = context->GetIsolate();
2212 Handle<Context> native_context(context->native_context(), isolate);
2213
2214 // Raise an EvalError if we did not receive a string.
2215 if (source.is_null()) {
2216 Handle<Object> error_message =
2217 native_context->ErrorMessageForCodeGenerationFromStrings();
2218 THROW_NEW_ERROR(
2219 isolate,
2220 NewEvalError(MessageTemplate::kCodeGenFromStrings, error_message),
2221 JSFunction);
2222 }
2223
2224 // Compile source string in the native context.
2225 int eval_scope_position = 0;
2226 int eval_position = kNoSourcePosition;
2227 Handle<SharedFunctionInfo> outer_info(
2228 native_context->empty_function().shared(), isolate);
2229 return Compiler::GetFunctionFromEval(source.ToHandleChecked(), outer_info,
2230 native_context, LanguageMode::kSloppy,
2231 restriction, parameters_end_pos,
2232 eval_scope_position, eval_position);
2233 }
2234
2235 // static
GetFunctionFromString(Handle<Context> context,Handle<Object> source,ParseRestriction restriction,int parameters_end_pos,bool is_code_like)2236 MaybeHandle<JSFunction> Compiler::GetFunctionFromString(
2237 Handle<Context> context, Handle<Object> source,
2238 ParseRestriction restriction, int parameters_end_pos, bool is_code_like) {
2239 Isolate* const isolate = context->GetIsolate();
2240 MaybeHandle<String> validated_source =
2241 ValidateDynamicCompilationSource(isolate, context, source, is_code_like)
2242 .first;
2243 return GetFunctionFromValidatedString(context, validated_source, restriction,
2244 parameters_end_pos);
2245 }
2246
2247 namespace {
2248
2249 struct ScriptCompileTimerScope {
2250 public:
2251 // TODO(leszeks): There are too many blink-specific entries in this enum,
2252 // figure out a way to push produce/hit-isolate-cache/consume/consume-failed
2253 // back up the API and log them in blink instead.
2254 enum class CacheBehaviour {
2255 kProduceCodeCache,
2256 kHitIsolateCacheWhenNoCache,
2257 kConsumeCodeCache,
2258 kConsumeCodeCacheFailed,
2259 kNoCacheBecauseInlineScript,
2260 kNoCacheBecauseScriptTooSmall,
2261 kNoCacheBecauseCacheTooCold,
2262 kNoCacheNoReason,
2263 kNoCacheBecauseNoResource,
2264 kNoCacheBecauseInspector,
2265 kNoCacheBecauseCachingDisabled,
2266 kNoCacheBecauseModule,
2267 kNoCacheBecauseStreamingSource,
2268 kNoCacheBecauseV8Extension,
2269 kHitIsolateCacheWhenProduceCodeCache,
2270 kHitIsolateCacheWhenConsumeCodeCache,
2271 kNoCacheBecauseExtensionModule,
2272 kNoCacheBecausePacScript,
2273 kNoCacheBecauseInDocumentWrite,
2274 kNoCacheBecauseResourceWithNoCacheHandler,
2275 kHitIsolateCacheWhenStreamingSource,
2276 kCount
2277 };
2278
ScriptCompileTimerScopev8::internal::__anon75b411e30611::ScriptCompileTimerScope2279 explicit ScriptCompileTimerScope(
2280 Isolate* isolate, ScriptCompiler::NoCacheReason no_cache_reason)
2281 : isolate_(isolate),
2282 all_scripts_histogram_scope_(isolate->counters()->compile_script(),
2283 true),
2284 no_cache_reason_(no_cache_reason),
2285 hit_isolate_cache_(false),
2286 producing_code_cache_(false),
2287 consuming_code_cache_(false),
2288 consuming_code_cache_failed_(false) {}
2289
~ScriptCompileTimerScopev8::internal::__anon75b411e30611::ScriptCompileTimerScope2290 ~ScriptCompileTimerScope() {
2291 CacheBehaviour cache_behaviour = GetCacheBehaviour();
2292
2293 Histogram* cache_behaviour_histogram =
2294 isolate_->counters()->compile_script_cache_behaviour();
2295 // Sanity check that the histogram has exactly one bin per enum entry.
2296 DCHECK_EQ(0, cache_behaviour_histogram->min());
2297 DCHECK_EQ(static_cast<int>(CacheBehaviour::kCount),
2298 cache_behaviour_histogram->max() + 1);
2299 DCHECK_EQ(static_cast<int>(CacheBehaviour::kCount),
2300 cache_behaviour_histogram->num_buckets());
2301 cache_behaviour_histogram->AddSample(static_cast<int>(cache_behaviour));
2302
2303 histogram_scope_.set_histogram(
2304 GetCacheBehaviourTimedHistogram(cache_behaviour));
2305 }
2306
set_hit_isolate_cachev8::internal::__anon75b411e30611::ScriptCompileTimerScope2307 void set_hit_isolate_cache() { hit_isolate_cache_ = true; }
2308
set_producing_code_cachev8::internal::__anon75b411e30611::ScriptCompileTimerScope2309 void set_producing_code_cache() { producing_code_cache_ = true; }
2310
set_consuming_code_cachev8::internal::__anon75b411e30611::ScriptCompileTimerScope2311 void set_consuming_code_cache() { consuming_code_cache_ = true; }
2312
set_consuming_code_cache_failedv8::internal::__anon75b411e30611::ScriptCompileTimerScope2313 void set_consuming_code_cache_failed() {
2314 consuming_code_cache_failed_ = true;
2315 }
2316
2317 private:
2318 Isolate* isolate_;
2319 LazyTimedHistogramScope histogram_scope_;
2320 // TODO(leszeks): This timer is the sum of the other times, consider removing
2321 // it to save space.
2322 HistogramTimerScope all_scripts_histogram_scope_;
2323 ScriptCompiler::NoCacheReason no_cache_reason_;
2324 bool hit_isolate_cache_;
2325 bool producing_code_cache_;
2326 bool consuming_code_cache_;
2327 bool consuming_code_cache_failed_;
2328
GetCacheBehaviourv8::internal::__anon75b411e30611::ScriptCompileTimerScope2329 CacheBehaviour GetCacheBehaviour() {
2330 if (producing_code_cache_) {
2331 if (hit_isolate_cache_) {
2332 return CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache;
2333 } else {
2334 return CacheBehaviour::kProduceCodeCache;
2335 }
2336 }
2337
2338 if (consuming_code_cache_) {
2339 if (hit_isolate_cache_) {
2340 return CacheBehaviour::kHitIsolateCacheWhenConsumeCodeCache;
2341 } else if (consuming_code_cache_failed_) {
2342 return CacheBehaviour::kConsumeCodeCacheFailed;
2343 }
2344 return CacheBehaviour::kConsumeCodeCache;
2345 }
2346
2347 if (hit_isolate_cache_) {
2348 if (no_cache_reason_ == ScriptCompiler::kNoCacheBecauseStreamingSource) {
2349 return CacheBehaviour::kHitIsolateCacheWhenStreamingSource;
2350 }
2351 return CacheBehaviour::kHitIsolateCacheWhenNoCache;
2352 }
2353
2354 switch (no_cache_reason_) {
2355 case ScriptCompiler::kNoCacheBecauseInlineScript:
2356 return CacheBehaviour::kNoCacheBecauseInlineScript;
2357 case ScriptCompiler::kNoCacheBecauseScriptTooSmall:
2358 return CacheBehaviour::kNoCacheBecauseScriptTooSmall;
2359 case ScriptCompiler::kNoCacheBecauseCacheTooCold:
2360 return CacheBehaviour::kNoCacheBecauseCacheTooCold;
2361 case ScriptCompiler::kNoCacheNoReason:
2362 return CacheBehaviour::kNoCacheNoReason;
2363 case ScriptCompiler::kNoCacheBecauseNoResource:
2364 return CacheBehaviour::kNoCacheBecauseNoResource;
2365 case ScriptCompiler::kNoCacheBecauseInspector:
2366 return CacheBehaviour::kNoCacheBecauseInspector;
2367 case ScriptCompiler::kNoCacheBecauseCachingDisabled:
2368 return CacheBehaviour::kNoCacheBecauseCachingDisabled;
2369 case ScriptCompiler::kNoCacheBecauseModule:
2370 return CacheBehaviour::kNoCacheBecauseModule;
2371 case ScriptCompiler::kNoCacheBecauseStreamingSource:
2372 return CacheBehaviour::kNoCacheBecauseStreamingSource;
2373 case ScriptCompiler::kNoCacheBecauseV8Extension:
2374 return CacheBehaviour::kNoCacheBecauseV8Extension;
2375 case ScriptCompiler::kNoCacheBecauseExtensionModule:
2376 return CacheBehaviour::kNoCacheBecauseExtensionModule;
2377 case ScriptCompiler::kNoCacheBecausePacScript:
2378 return CacheBehaviour::kNoCacheBecausePacScript;
2379 case ScriptCompiler::kNoCacheBecauseInDocumentWrite:
2380 return CacheBehaviour::kNoCacheBecauseInDocumentWrite;
2381 case ScriptCompiler::kNoCacheBecauseResourceWithNoCacheHandler:
2382 return CacheBehaviour::kNoCacheBecauseResourceWithNoCacheHandler;
2383 case ScriptCompiler::kNoCacheBecauseDeferredProduceCodeCache: {
2384 if (hit_isolate_cache_) {
2385 return CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache;
2386 } else {
2387 return CacheBehaviour::kProduceCodeCache;
2388 }
2389 }
2390 }
2391 UNREACHABLE();
2392 }
2393
GetCacheBehaviourTimedHistogramv8::internal::__anon75b411e30611::ScriptCompileTimerScope2394 TimedHistogram* GetCacheBehaviourTimedHistogram(
2395 CacheBehaviour cache_behaviour) {
2396 switch (cache_behaviour) {
2397 case CacheBehaviour::kProduceCodeCache:
2398 // Even if we hit the isolate's compilation cache, we currently recompile
2399 // when we want to produce the code cache.
2400 case CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache:
2401 return isolate_->counters()->compile_script_with_produce_cache();
2402 case CacheBehaviour::kHitIsolateCacheWhenNoCache:
2403 case CacheBehaviour::kHitIsolateCacheWhenConsumeCodeCache:
2404 case CacheBehaviour::kHitIsolateCacheWhenStreamingSource:
2405 return isolate_->counters()->compile_script_with_isolate_cache_hit();
2406 case CacheBehaviour::kConsumeCodeCacheFailed:
2407 return isolate_->counters()->compile_script_consume_failed();
2408 case CacheBehaviour::kConsumeCodeCache:
2409 return isolate_->counters()->compile_script_with_consume_cache();
2410
2411 // Note that this only counts the finalization part of streaming, the
2412 // actual streaming compile is counted by BackgroundCompileTask into
2413 // "compile_script_on_background".
2414 case CacheBehaviour::kNoCacheBecauseStreamingSource:
2415 return isolate_->counters()->compile_script_streaming_finalization();
2416
2417 case CacheBehaviour::kNoCacheBecauseInlineScript:
2418 return isolate_->counters()
2419 ->compile_script_no_cache_because_inline_script();
2420 case CacheBehaviour::kNoCacheBecauseScriptTooSmall:
2421 return isolate_->counters()
2422 ->compile_script_no_cache_because_script_too_small();
2423 case CacheBehaviour::kNoCacheBecauseCacheTooCold:
2424 return isolate_->counters()
2425 ->compile_script_no_cache_because_cache_too_cold();
2426
2427 // Aggregate all the other "no cache" counters into a single histogram, to
2428 // save space.
2429 case CacheBehaviour::kNoCacheNoReason:
2430 case CacheBehaviour::kNoCacheBecauseNoResource:
2431 case CacheBehaviour::kNoCacheBecauseInspector:
2432 case CacheBehaviour::kNoCacheBecauseCachingDisabled:
2433 // TODO(leszeks): Consider counting separately once modules are more
2434 // common.
2435 case CacheBehaviour::kNoCacheBecauseModule:
2436 case CacheBehaviour::kNoCacheBecauseV8Extension:
2437 case CacheBehaviour::kNoCacheBecauseExtensionModule:
2438 case CacheBehaviour::kNoCacheBecausePacScript:
2439 case CacheBehaviour::kNoCacheBecauseInDocumentWrite:
2440 case CacheBehaviour::kNoCacheBecauseResourceWithNoCacheHandler:
2441 return isolate_->counters()->compile_script_no_cache_other();
2442
2443 case CacheBehaviour::kCount:
2444 UNREACHABLE();
2445 }
2446 UNREACHABLE();
2447 }
2448 };
2449
SetScriptFieldsFromDetails(Isolate * isolate,Script script,Compiler::ScriptDetails script_details,DisallowHeapAllocation * no_gc)2450 void SetScriptFieldsFromDetails(Isolate* isolate, Script script,
2451 Compiler::ScriptDetails script_details,
2452 DisallowHeapAllocation* no_gc) {
2453 Handle<Object> script_name;
2454 if (script_details.name_obj.ToHandle(&script_name)) {
2455 script.set_name(*script_name);
2456 script.set_line_offset(script_details.line_offset);
2457 script.set_column_offset(script_details.column_offset);
2458 }
2459 // The API can provide a source map URL, but a source map URL could also have
2460 // been inferred by the parser from a magic comment. The latter takes
2461 // preference over the former, so we don't want to override the source mapping
2462 // URL if it already exists.
2463 Handle<Object> source_map_url;
2464 if (script_details.source_map_url.ToHandle(&source_map_url) &&
2465 script.source_mapping_url(isolate).IsUndefined(isolate)) {
2466 script.set_source_mapping_url(*source_map_url);
2467 }
2468 Handle<FixedArray> host_defined_options;
2469 if (script_details.host_defined_options.ToHandle(&host_defined_options)) {
2470 script.set_host_defined_options(*host_defined_options);
2471 }
2472 }
2473
NewScript(Isolate * isolate,ParseInfo * parse_info,Handle<String> source,Compiler::ScriptDetails script_details,ScriptOriginOptions origin_options,NativesFlag natives,MaybeHandle<FixedArray> maybe_wrapped_arguments=kNullMaybeHandle)2474 Handle<Script> NewScript(
2475 Isolate* isolate, ParseInfo* parse_info, Handle<String> source,
2476 Compiler::ScriptDetails script_details, ScriptOriginOptions origin_options,
2477 NativesFlag natives,
2478 MaybeHandle<FixedArray> maybe_wrapped_arguments = kNullMaybeHandle) {
2479 // Create a script object describing the script to be compiled.
2480 Handle<Script> script = parse_info->CreateScript(
2481 isolate, source, maybe_wrapped_arguments, origin_options, natives);
2482 DisallowHeapAllocation no_gc;
2483 SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
2484 LOG(isolate, ScriptDetails(*script));
2485 return script;
2486 }
2487
CompileScriptOnMainThread(const UnoptimizedCompileFlags flags,Handle<String> source,const Compiler::ScriptDetails & script_details,ScriptOriginOptions origin_options,NativesFlag natives,v8::Extension * extension,Isolate * isolate,IsCompiledScope * is_compiled_scope)2488 MaybeHandle<SharedFunctionInfo> CompileScriptOnMainThread(
2489 const UnoptimizedCompileFlags flags, Handle<String> source,
2490 const Compiler::ScriptDetails& script_details,
2491 ScriptOriginOptions origin_options, NativesFlag natives,
2492 v8::Extension* extension, Isolate* isolate,
2493 IsCompiledScope* is_compiled_scope) {
2494 UnoptimizedCompileState compile_state(isolate);
2495 ParseInfo parse_info(isolate, flags, &compile_state);
2496 parse_info.set_extension(extension);
2497
2498 Handle<Script> script = NewScript(isolate, &parse_info, source,
2499 script_details, origin_options, natives);
2500 DCHECK_IMPLIES(parse_info.flags().collect_type_profile(),
2501 script->IsUserJavaScript());
2502 DCHECK_EQ(parse_info.flags().is_repl_mode(), script->is_repl_mode());
2503
2504 return CompileToplevel(&parse_info, script, isolate, is_compiled_scope);
2505 }
2506
2507 class StressBackgroundCompileThread : public base::Thread {
2508 public:
StressBackgroundCompileThread(Isolate * isolate,Handle<String> source)2509 StressBackgroundCompileThread(Isolate* isolate, Handle<String> source)
2510 : base::Thread(
2511 base::Thread::Options("StressBackgroundCompileThread", 2 * i::MB)),
2512 source_(source),
2513 streamed_source_(std::make_unique<SourceStream>(source, isolate),
2514 v8::ScriptCompiler::StreamedSource::UTF8) {
2515 data()->task = std::make_unique<i::BackgroundCompileTask>(data(), isolate);
2516 }
2517
Run()2518 void Run() override { data()->task->Run(); }
2519
data()2520 ScriptStreamingData* data() { return streamed_source_.impl(); }
2521
2522 private:
2523 // Dummy external source stream which returns the whole source in one go.
2524 // TODO(leszeks): Also test chunking the data.
2525 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream {
2526 public:
SourceStream(Handle<String> source,Isolate * isolate)2527 SourceStream(Handle<String> source, Isolate* isolate) : done_(false) {
2528 source_buffer_ = source->ToCString(ALLOW_NULLS, FAST_STRING_TRAVERSAL,
2529 &source_length_);
2530 }
2531
GetMoreData(const uint8_t ** src)2532 size_t GetMoreData(const uint8_t** src) override {
2533 if (done_) {
2534 return 0;
2535 }
2536 *src = reinterpret_cast<uint8_t*>(source_buffer_.release());
2537 done_ = true;
2538
2539 return source_length_;
2540 }
2541
2542 private:
2543 int source_length_;
2544 std::unique_ptr<char[]> source_buffer_;
2545 bool done_;
2546 };
2547
2548 Handle<String> source_;
2549 v8::ScriptCompiler::StreamedSource streamed_source_;
2550 };
2551
CanBackgroundCompile(const Compiler::ScriptDetails & script_details,ScriptOriginOptions origin_options,v8::Extension * extension,ScriptCompiler::CompileOptions compile_options,NativesFlag natives)2552 bool CanBackgroundCompile(const Compiler::ScriptDetails& script_details,
2553 ScriptOriginOptions origin_options,
2554 v8::Extension* extension,
2555 ScriptCompiler::CompileOptions compile_options,
2556 NativesFlag natives) {
2557 // TODO(leszeks): Remove the module check once background compilation of
2558 // modules is supported.
2559 return !origin_options.IsModule() && !extension &&
2560 script_details.repl_mode == REPLMode::kNo &&
2561 compile_options == ScriptCompiler::kNoCompileOptions &&
2562 natives == NOT_NATIVES_CODE;
2563 }
2564
CompilationExceptionIsRangeError(Isolate * isolate,Handle<Object> obj)2565 bool CompilationExceptionIsRangeError(Isolate* isolate, Handle<Object> obj) {
2566 if (!obj->IsJSError(isolate)) return false;
2567 Handle<JSReceiver> js_obj = Handle<JSReceiver>::cast(obj);
2568 Handle<JSReceiver> constructor;
2569 if (!JSReceiver::GetConstructor(js_obj).ToHandle(&constructor)) {
2570 return false;
2571 }
2572 return *constructor == *isolate->range_error_function();
2573 }
2574
CompileScriptOnBothBackgroundAndMainThread(Handle<String> source,const Compiler::ScriptDetails & script_details,ScriptOriginOptions origin_options,Isolate * isolate,IsCompiledScope * is_compiled_scope)2575 MaybeHandle<SharedFunctionInfo> CompileScriptOnBothBackgroundAndMainThread(
2576 Handle<String> source, const Compiler::ScriptDetails& script_details,
2577 ScriptOriginOptions origin_options, Isolate* isolate,
2578 IsCompiledScope* is_compiled_scope) {
2579 // Start a background thread compiling the script.
2580 StressBackgroundCompileThread background_compile_thread(isolate, source);
2581
2582 UnoptimizedCompileFlags flags_copy =
2583 background_compile_thread.data()->task->flags();
2584
2585 CHECK(background_compile_thread.Start());
2586 MaybeHandle<SharedFunctionInfo> main_thread_maybe_result;
2587 bool main_thread_had_stack_overflow = false;
2588 // In parallel, compile on the main thread to flush out any data races.
2589 {
2590 IsCompiledScope inner_is_compiled_scope;
2591 // The background thread should also create any relevant exceptions, so we
2592 // can ignore the main-thread created ones.
2593 // TODO(leszeks): Maybe verify that any thrown (or unthrown) exceptions are
2594 // equivalent.
2595 TryCatch ignore_try_catch(reinterpret_cast<v8::Isolate*>(isolate));
2596 flags_copy.set_script_id(Script::kTemporaryScriptId);
2597 main_thread_maybe_result = CompileScriptOnMainThread(
2598 flags_copy, source, script_details, origin_options, NOT_NATIVES_CODE,
2599 nullptr, isolate, &inner_is_compiled_scope);
2600 if (main_thread_maybe_result.is_null()) {
2601 // Assume all range errors are stack overflows.
2602 main_thread_had_stack_overflow = CompilationExceptionIsRangeError(
2603 isolate, handle(isolate->pending_exception(), isolate));
2604 isolate->clear_pending_exception();
2605 }
2606 }
2607
2608 // Join with background thread and finalize compilation.
2609 background_compile_thread.Join();
2610 MaybeHandle<SharedFunctionInfo> maybe_result =
2611 Compiler::GetSharedFunctionInfoForStreamedScript(
2612 isolate, source, script_details, origin_options,
2613 background_compile_thread.data());
2614
2615 // Either both compiles should succeed, or both should fail. The one exception
2616 // to this is that the main-thread compilation might stack overflow while the
2617 // background compilation doesn't, so relax the check to include this case.
2618 // TODO(leszeks): Compare the contents of the results of the two compiles.
2619 if (main_thread_had_stack_overflow) {
2620 CHECK(main_thread_maybe_result.is_null());
2621 } else {
2622 CHECK_EQ(maybe_result.is_null(), main_thread_maybe_result.is_null());
2623 }
2624
2625 Handle<SharedFunctionInfo> result;
2626 if (maybe_result.ToHandle(&result)) {
2627 // The BackgroundCompileTask's IsCompiledScope will keep the result alive
2628 // until it dies at the end of this function, after which this new
2629 // IsCompiledScope can take over.
2630 *is_compiled_scope = result->is_compiled_scope(isolate);
2631 }
2632
2633 return maybe_result;
2634 }
2635
2636 } // namespace
2637
2638 // static
GetSharedFunctionInfoForScript(Isolate * isolate,Handle<String> source,const Compiler::ScriptDetails & script_details,ScriptOriginOptions origin_options,v8::Extension * extension,ScriptData * cached_data,ScriptCompiler::CompileOptions compile_options,ScriptCompiler::NoCacheReason no_cache_reason,NativesFlag natives)2639 MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript(
2640 Isolate* isolate, Handle<String> source,
2641 const Compiler::ScriptDetails& script_details,
2642 ScriptOriginOptions origin_options, v8::Extension* extension,
2643 ScriptData* cached_data, ScriptCompiler::CompileOptions compile_options,
2644 ScriptCompiler::NoCacheReason no_cache_reason, NativesFlag natives) {
2645 ScriptCompileTimerScope compile_timer(isolate, no_cache_reason);
2646
2647 if (compile_options == ScriptCompiler::kNoCompileOptions ||
2648 compile_options == ScriptCompiler::kEagerCompile) {
2649 DCHECK_NULL(cached_data);
2650 } else {
2651 DCHECK(compile_options == ScriptCompiler::kConsumeCodeCache);
2652 DCHECK(cached_data);
2653 DCHECK_NULL(extension);
2654 }
2655 int source_length = source->length();
2656 isolate->counters()->total_load_size()->Increment(source_length);
2657 isolate->counters()->total_compile_size()->Increment(source_length);
2658
2659 LanguageMode language_mode = construct_language_mode(FLAG_use_strict);
2660 CompilationCache* compilation_cache = isolate->compilation_cache();
2661
2662 // For extensions or REPL mode scripts neither do a compilation cache lookup,
2663 // nor put the compilation result back into the cache.
2664 const bool use_compilation_cache =
2665 extension == nullptr && script_details.repl_mode == REPLMode::kNo;
2666 MaybeHandle<SharedFunctionInfo> maybe_result;
2667 IsCompiledScope is_compiled_scope;
2668 if (use_compilation_cache) {
2669 bool can_consume_code_cache =
2670 compile_options == ScriptCompiler::kConsumeCodeCache;
2671 if (can_consume_code_cache) {
2672 compile_timer.set_consuming_code_cache();
2673 }
2674
2675 // First check per-isolate compilation cache.
2676 maybe_result = compilation_cache->LookupScript(
2677 source, script_details.name_obj, script_details.line_offset,
2678 script_details.column_offset, origin_options, isolate->native_context(),
2679 language_mode);
2680 if (!maybe_result.is_null()) {
2681 compile_timer.set_hit_isolate_cache();
2682 } else if (can_consume_code_cache) {
2683 compile_timer.set_consuming_code_cache();
2684 // Then check cached code provided by embedder.
2685 HistogramTimerScope timer(isolate->counters()->compile_deserialize());
2686 RuntimeCallTimerScope runtimeTimer(
2687 isolate, RuntimeCallCounterId::kCompileDeserialize);
2688 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2689 "V8.CompileDeserialize");
2690 Handle<SharedFunctionInfo> inner_result;
2691 if (CodeSerializer::Deserialize(isolate, cached_data, source,
2692 origin_options)
2693 .ToHandle(&inner_result) &&
2694 inner_result->is_compiled()) {
2695 // Promote to per-isolate compilation cache.
2696 is_compiled_scope = inner_result->is_compiled_scope(isolate);
2697 DCHECK(is_compiled_scope.is_compiled());
2698 compilation_cache->PutScript(source, isolate->native_context(),
2699 language_mode, inner_result);
2700 Handle<Script> script(Script::cast(inner_result->script()), isolate);
2701 maybe_result = inner_result;
2702 } else {
2703 // Deserializer failed. Fall through to compile.
2704 compile_timer.set_consuming_code_cache_failed();
2705 }
2706 }
2707 }
2708
2709 if (maybe_result.is_null()) {
2710 // No cache entry found compile the script.
2711 if (FLAG_stress_background_compile &&
2712 CanBackgroundCompile(script_details, origin_options, extension,
2713 compile_options, natives)) {
2714 // If the --stress-background-compile flag is set, do the actual
2715 // compilation on a background thread, and wait for its result.
2716 maybe_result = CompileScriptOnBothBackgroundAndMainThread(
2717 source, script_details, origin_options, isolate, &is_compiled_scope);
2718 } else {
2719 UnoptimizedCompileFlags flags =
2720 UnoptimizedCompileFlags::ForToplevelCompile(
2721 isolate, natives == NOT_NATIVES_CODE, language_mode,
2722 script_details.repl_mode);
2723
2724 flags.set_is_eager(compile_options == ScriptCompiler::kEagerCompile);
2725 flags.set_is_module(origin_options.IsModule());
2726
2727 maybe_result = CompileScriptOnMainThread(
2728 flags, source, script_details, origin_options, natives, extension,
2729 isolate, &is_compiled_scope);
2730 }
2731
2732 // Add the result to the isolate cache.
2733 Handle<SharedFunctionInfo> result;
2734 if (use_compilation_cache && maybe_result.ToHandle(&result)) {
2735 DCHECK(is_compiled_scope.is_compiled());
2736 compilation_cache->PutScript(source, isolate->native_context(),
2737 language_mode, result);
2738 } else if (maybe_result.is_null() && natives != EXTENSION_CODE) {
2739 isolate->ReportPendingMessages();
2740 }
2741 }
2742
2743 return maybe_result;
2744 }
2745
2746 // static
GetWrappedFunction(Handle<String> source,Handle<FixedArray> arguments,Handle<Context> context,const Compiler::ScriptDetails & script_details,ScriptOriginOptions origin_options,ScriptData * cached_data,v8::ScriptCompiler::CompileOptions compile_options,v8::ScriptCompiler::NoCacheReason no_cache_reason)2747 MaybeHandle<JSFunction> Compiler::GetWrappedFunction(
2748 Handle<String> source, Handle<FixedArray> arguments,
2749 Handle<Context> context, const Compiler::ScriptDetails& script_details,
2750 ScriptOriginOptions origin_options, ScriptData* cached_data,
2751 v8::ScriptCompiler::CompileOptions compile_options,
2752 v8::ScriptCompiler::NoCacheReason no_cache_reason) {
2753 Isolate* isolate = context->GetIsolate();
2754 ScriptCompileTimerScope compile_timer(isolate, no_cache_reason);
2755
2756 if (compile_options == ScriptCompiler::kNoCompileOptions ||
2757 compile_options == ScriptCompiler::kEagerCompile) {
2758 DCHECK_NULL(cached_data);
2759 } else {
2760 DCHECK(compile_options == ScriptCompiler::kConsumeCodeCache);
2761 DCHECK(cached_data);
2762 }
2763
2764 int source_length = source->length();
2765 isolate->counters()->total_compile_size()->Increment(source_length);
2766
2767 LanguageMode language_mode = construct_language_mode(FLAG_use_strict);
2768
2769 MaybeHandle<SharedFunctionInfo> maybe_result;
2770 bool can_consume_code_cache =
2771 compile_options == ScriptCompiler::kConsumeCodeCache;
2772 if (can_consume_code_cache) {
2773 compile_timer.set_consuming_code_cache();
2774 // Then check cached code provided by embedder.
2775 HistogramTimerScope timer(isolate->counters()->compile_deserialize());
2776 RuntimeCallTimerScope runtimeTimer(
2777 isolate, RuntimeCallCounterId::kCompileDeserialize);
2778 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2779 "V8.CompileDeserialize");
2780 maybe_result = CodeSerializer::Deserialize(isolate, cached_data, source,
2781 origin_options);
2782 if (maybe_result.is_null()) {
2783 // Deserializer failed. Fall through to compile.
2784 compile_timer.set_consuming_code_cache_failed();
2785 }
2786 }
2787
2788 Handle<SharedFunctionInfo> wrapped;
2789 Handle<Script> script;
2790 IsCompiledScope is_compiled_scope;
2791 if (!maybe_result.ToHandle(&wrapped)) {
2792 UnoptimizedCompileFlags flags = UnoptimizedCompileFlags::ForToplevelCompile(
2793 isolate, true, language_mode, script_details.repl_mode);
2794 flags.set_is_eval(true); // Use an eval scope as declaration scope.
2795 flags.set_function_syntax_kind(FunctionSyntaxKind::kWrapped);
2796 // TODO(delphick): Remove this and instead make the wrapped and wrapper
2797 // functions fully non-lazy instead thus preventing source positions from
2798 // being omitted.
2799 flags.set_collect_source_positions(true);
2800 // flags.set_eager(compile_options == ScriptCompiler::kEagerCompile);
2801
2802 UnoptimizedCompileState compile_state(isolate);
2803 ParseInfo parse_info(isolate, flags, &compile_state);
2804
2805 MaybeHandle<ScopeInfo> maybe_outer_scope_info;
2806 if (!context->IsNativeContext()) {
2807 maybe_outer_scope_info = handle(context->scope_info(), isolate);
2808 }
2809
2810 script = NewScript(isolate, &parse_info, source, script_details,
2811 origin_options, NOT_NATIVES_CODE, arguments);
2812
2813 Handle<SharedFunctionInfo> top_level;
2814 maybe_result = CompileToplevel(&parse_info, script, maybe_outer_scope_info,
2815 isolate, &is_compiled_scope);
2816 if (maybe_result.is_null()) isolate->ReportPendingMessages();
2817 ASSIGN_RETURN_ON_EXCEPTION(isolate, top_level, maybe_result, JSFunction);
2818
2819 SharedFunctionInfo::ScriptIterator infos(isolate, *script);
2820 for (SharedFunctionInfo info = infos.Next(); !info.is_null();
2821 info = infos.Next()) {
2822 if (info.is_wrapped()) {
2823 wrapped = Handle<SharedFunctionInfo>(info, isolate);
2824 break;
2825 }
2826 }
2827 DCHECK(!wrapped.is_null());
2828 } else {
2829 is_compiled_scope = wrapped->is_compiled_scope(isolate);
2830 script = Handle<Script>(Script::cast(wrapped->script()), isolate);
2831 }
2832 DCHECK(is_compiled_scope.is_compiled());
2833
2834 return isolate->factory()->NewFunctionFromSharedFunctionInfo(
2835 wrapped, context, AllocationType::kYoung);
2836 }
2837
2838 // static
2839 MaybeHandle<SharedFunctionInfo>
GetSharedFunctionInfoForStreamedScript(Isolate * isolate,Handle<String> source,const ScriptDetails & script_details,ScriptOriginOptions origin_options,ScriptStreamingData * streaming_data)2840 Compiler::GetSharedFunctionInfoForStreamedScript(
2841 Isolate* isolate, Handle<String> source,
2842 const ScriptDetails& script_details, ScriptOriginOptions origin_options,
2843 ScriptStreamingData* streaming_data) {
2844 DCHECK(!origin_options.IsModule());
2845 DCHECK(!origin_options.IsWasm());
2846
2847 ScriptCompileTimerScope compile_timer(
2848 isolate, ScriptCompiler::kNoCacheBecauseStreamingSource);
2849 PostponeInterruptsScope postpone(isolate);
2850
2851 int source_length = source->length();
2852 isolate->counters()->total_load_size()->Increment(source_length);
2853 isolate->counters()->total_compile_size()->Increment(source_length);
2854
2855 BackgroundCompileTask* task = streaming_data->task.get();
2856
2857 MaybeHandle<SharedFunctionInfo> maybe_result;
2858 // Check if compile cache already holds the SFI, if so no need to finalize
2859 // the code compiled on the background thread.
2860 CompilationCache* compilation_cache = isolate->compilation_cache();
2861 {
2862 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2863 "V8.StreamingFinalization.CheckCache");
2864 maybe_result = compilation_cache->LookupScript(
2865 source, script_details.name_obj, script_details.line_offset,
2866 script_details.column_offset, origin_options, isolate->native_context(),
2867 task->language_mode());
2868 if (!maybe_result.is_null()) {
2869 compile_timer.set_hit_isolate_cache();
2870 }
2871 }
2872
2873 if (maybe_result.is_null()) {
2874 // No cache entry found, finalize compilation of the script and add it to
2875 // the isolate cache.
2876
2877 Handle<Script> script;
2878 if (FLAG_finalize_streaming_on_background) {
2879 RuntimeCallTimerScope runtimeTimerScope(
2880 isolate, RuntimeCallCounterId::kCompilePublishBackgroundFinalization);
2881 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2882 "V8.OffThreadFinalization.Publish");
2883
2884 script = task->GetScript(isolate);
2885
2886 // We might not have been able to finalize all jobs on the background
2887 // thread (e.g. asm.js jobs), so finalize those deferred jobs now.
2888 if (FinalizeDeferredUnoptimizedCompilationJobs(
2889 isolate, script,
2890 task->jobs_to_retry_finalization_on_main_thread(),
2891 task->compile_state()->pending_error_handler(),
2892 task->finalize_unoptimized_compilation_data())) {
2893 maybe_result = task->GetOuterFunctionSfi(isolate);
2894 }
2895
2896 script->set_source(*source);
2897 script->set_origin_options(origin_options);
2898
2899 // The one post-hoc fix-up: Add the script to the script list.
2900 Handle<WeakArrayList> scripts = isolate->factory()->script_list();
2901 scripts = WeakArrayList::Append(isolate, scripts,
2902 MaybeObjectHandle::Weak(script));
2903 isolate->heap()->SetRootScriptList(*scripts);
2904 } else {
2905 ParseInfo* parse_info = task->info();
2906 DCHECK(parse_info->flags().is_toplevel());
2907
2908 script = parse_info->CreateScript(isolate, source, kNullMaybeHandle,
2909 origin_options);
2910
2911 task->parser()->UpdateStatistics(isolate, script);
2912 task->parser()->HandleSourceURLComments(isolate, script);
2913
2914 if (!task->compilation_jobs()->empty()) {
2915 // Off-thread parse & compile has succeeded - finalize compilation.
2916 DCHECK_NOT_NULL(parse_info->literal());
2917
2918 parse_info->ast_value_factory()->Internalize(isolate);
2919
2920 Handle<SharedFunctionInfo> shared_info =
2921 CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
2922 if (FinalizeAllUnoptimizedCompilationJobs(
2923 parse_info, isolate, script, task->compilation_jobs(),
2924 task->finalize_unoptimized_compilation_data())) {
2925 maybe_result = shared_info;
2926 }
2927 }
2928
2929 if (maybe_result.is_null()) {
2930 // Compilation failed - prepare to throw an exception after script
2931 // fields have been set.
2932 PreparePendingException(isolate, parse_info);
2933 }
2934 }
2935
2936 // Set the script fields after finalization, to keep this path the same
2937 // between main-thread and off-thread finalization.
2938 {
2939 DisallowHeapAllocation no_gc;
2940 SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
2941 LOG(isolate, ScriptDetails(*script));
2942 }
2943
2944 Handle<SharedFunctionInfo> result;
2945 if (!maybe_result.ToHandle(&result)) {
2946 FailWithPreparedPendingException(
2947 isolate, script, task->compile_state()->pending_error_handler());
2948 } else {
2949 FinalizeUnoptimizedScriptCompilation(
2950 isolate, script, task->flags(), task->compile_state(),
2951 *task->finalize_unoptimized_compilation_data());
2952
2953 // Add compiled code to the isolate cache.
2954 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2955 "V8.StreamingFinalization.AddToCache");
2956 compilation_cache->PutScript(source, isolate->native_context(),
2957 task->language_mode(), result);
2958 }
2959 }
2960
2961 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
2962 "V8.StreamingFinalization.Release");
2963 streaming_data->Release();
2964 return maybe_result;
2965 }
2966
2967 // static
2968 template <typename LocalIsolate>
GetSharedFunctionInfo(FunctionLiteral * literal,Handle<Script> script,LocalIsolate * isolate)2969 Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
2970 FunctionLiteral* literal, Handle<Script> script, LocalIsolate* isolate) {
2971 // Precondition: code has been parsed and scopes have been analyzed.
2972 MaybeHandle<SharedFunctionInfo> maybe_existing;
2973
2974 // Find any previously allocated shared function info for the given literal.
2975 maybe_existing =
2976 script->FindSharedFunctionInfo(isolate, literal->function_literal_id());
2977
2978 // If we found an existing shared function info, return it.
2979 Handle<SharedFunctionInfo> existing;
2980 if (maybe_existing.ToHandle(&existing)) {
2981 // If the function has been uncompiled (bytecode flushed) it will have lost
2982 // any preparsed data. If we produced preparsed data during this compile for
2983 // this function, replace the uncompiled data with one that includes it.
2984 if (literal->produced_preparse_data() != nullptr &&
2985 existing->HasUncompiledDataWithoutPreparseData()) {
2986 Handle<UncompiledData> existing_uncompiled_data =
2987 handle(existing->uncompiled_data(), isolate);
2988 DCHECK_EQ(literal->start_position(),
2989 existing_uncompiled_data->start_position());
2990 DCHECK_EQ(literal->end_position(),
2991 existing_uncompiled_data->end_position());
2992 // Use existing uncompiled data's inferred name as it may be more
2993 // accurate than the literal we preparsed.
2994 Handle<String> inferred_name =
2995 handle(existing_uncompiled_data->inferred_name(), isolate);
2996 Handle<PreparseData> preparse_data =
2997 literal->produced_preparse_data()->Serialize(isolate);
2998 Handle<UncompiledData> new_uncompiled_data =
2999 isolate->factory()->NewUncompiledDataWithPreparseData(
3000 inferred_name, existing_uncompiled_data->start_position(),
3001 existing_uncompiled_data->end_position(), preparse_data);
3002 existing->set_uncompiled_data(*new_uncompiled_data);
3003 }
3004 return existing;
3005 }
3006
3007 // Allocate a shared function info object which will be compiled lazily.
3008 Handle<SharedFunctionInfo> result =
3009 isolate->factory()->NewSharedFunctionInfoForLiteral(literal, script,
3010 false);
3011 return result;
3012 }
3013
3014 template Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
3015 FunctionLiteral* literal, Handle<Script> script, Isolate* isolate);
3016 template Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
3017 FunctionLiteral* literal, Handle<Script> script, LocalIsolate* isolate);
3018
3019 // static
GetOptimizedCodeForOSR(Handle<JSFunction> function,BailoutId osr_offset,JavaScriptFrame * osr_frame)3020 MaybeHandle<Code> Compiler::GetOptimizedCodeForOSR(Handle<JSFunction> function,
3021 BailoutId osr_offset,
3022 JavaScriptFrame* osr_frame) {
3023 DCHECK(!osr_offset.IsNone());
3024 DCHECK_NOT_NULL(osr_frame);
3025 return GetOptimizedCode(function, ConcurrencyMode::kNotConcurrent,
3026 CodeKindForTopTier(), osr_offset, osr_frame);
3027 }
3028
3029 // static
FinalizeOptimizedCompilationJob(OptimizedCompilationJob * job,Isolate * isolate)3030 bool Compiler::FinalizeOptimizedCompilationJob(OptimizedCompilationJob* job,
3031 Isolate* isolate) {
3032 VMState<COMPILER> state(isolate);
3033 // Take ownership of the job. Deleting the job also tears down the zone.
3034 std::unique_ptr<OptimizedCompilationJob> job_scope(job);
3035 OptimizedCompilationInfo* compilation_info = job->compilation_info();
3036
3037 TimerEventScope<TimerEventRecompileSynchronous> timer(isolate);
3038 RuntimeCallTimerScope runtimeTimer(
3039 isolate, RuntimeCallCounterId::kOptimizeConcurrentFinalize);
3040 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
3041 "V8.OptimizeConcurrentFinalize");
3042
3043 Handle<SharedFunctionInfo> shared = compilation_info->shared_info();
3044
3045 CodeKind code_kind = compilation_info->code_kind();
3046 const bool should_install_code_on_function =
3047 !IsForNativeContextIndependentCachingOnly(code_kind);
3048 if (should_install_code_on_function) {
3049 // Reset profiler ticks, function is no longer considered hot.
3050 compilation_info->closure()->feedback_vector().set_profiler_ticks(0);
3051 }
3052
3053 DCHECK(!shared->HasBreakInfo());
3054
3055 // 1) Optimization on the concurrent thread may have failed.
3056 // 2) The function may have already been optimized by OSR. Simply continue.
3057 // Except when OSR already disabled optimization for some reason.
3058 // 3) The code may have already been invalidated due to dependency change.
3059 // 4) Code generation may have failed.
3060 if (job->state() == CompilationJob::State::kReadyToFinalize) {
3061 if (shared->optimization_disabled()) {
3062 job->RetryOptimization(BailoutReason::kOptimizationDisabled);
3063 } else if (job->FinalizeJob(isolate) == CompilationJob::SUCCEEDED) {
3064 job->RecordCompilationStats(OptimizedCompilationJob::kConcurrent,
3065 isolate);
3066 job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG,
3067 isolate);
3068 InsertCodeIntoOptimizedCodeCache(compilation_info);
3069 InsertCodeIntoCompilationCache(isolate, compilation_info);
3070 CompilerTracer::TraceCompletedJob(isolate, compilation_info);
3071 if (should_install_code_on_function) {
3072 compilation_info->closure()->set_code(*compilation_info->code());
3073 }
3074 return CompilationJob::SUCCEEDED;
3075 }
3076 }
3077
3078 DCHECK_EQ(job->state(), CompilationJob::State::kFailed);
3079 CompilerTracer::TraceAbortedJob(isolate, compilation_info);
3080 compilation_info->closure()->set_code(shared->GetCode());
3081 // Clear the InOptimizationQueue marker, if it exists.
3082 if (UsesOptimizationMarker(code_kind) &&
3083 compilation_info->closure()->IsInOptimizationQueue()) {
3084 compilation_info->closure()->ClearOptimizationMarker();
3085 }
3086 return CompilationJob::FAILED;
3087 }
3088
3089 // static
PostInstantiation(Handle<JSFunction> function)3090 void Compiler::PostInstantiation(Handle<JSFunction> function) {
3091 Isolate* isolate = function->GetIsolate();
3092 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
3093 IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
3094
3095 // If code is compiled to bytecode (i.e., isn't asm.js), then allocate a
3096 // feedback and check for optimized code.
3097 if (is_compiled_scope.is_compiled() && shared->HasBytecodeArray()) {
3098 JSFunction::InitializeFeedbackCell(function, &is_compiled_scope);
3099
3100 Code code = function->has_feedback_vector()
3101 ? function->feedback_vector().optimized_code()
3102 : Code();
3103 if (!code.is_null()) {
3104 // Caching of optimized code enabled and optimized code found.
3105 DCHECK(!code.marked_for_deoptimization());
3106 DCHECK(function->shared().is_compiled());
3107 function->set_code(code);
3108 }
3109
3110 if (FLAG_always_opt && shared->allows_lazy_compilation() &&
3111 !shared->optimization_disabled() &&
3112 !function->HasAvailableOptimizedCode()) {
3113 CompilerTracer::TraceMarkForAlwaysOpt(isolate, function);
3114 JSFunction::EnsureFeedbackVector(function, &is_compiled_scope);
3115 function->MarkForOptimization(ConcurrencyMode::kNotConcurrent);
3116 }
3117 }
3118
3119 if (shared->is_toplevel() || shared->is_wrapped()) {
3120 // If it's a top-level script, report compilation to the debugger.
3121 Handle<Script> script(Script::cast(shared->script()), isolate);
3122 isolate->debug()->OnAfterCompile(script);
3123 }
3124 }
3125
3126 // ----------------------------------------------------------------------------
3127 // Implementation of ScriptStreamingData
3128
ScriptStreamingData(std::unique_ptr<ScriptCompiler::ExternalSourceStream> source_stream,ScriptCompiler::StreamedSource::Encoding encoding)3129 ScriptStreamingData::ScriptStreamingData(
3130 std::unique_ptr<ScriptCompiler::ExternalSourceStream> source_stream,
3131 ScriptCompiler::StreamedSource::Encoding encoding)
3132 : source_stream(std::move(source_stream)), encoding(encoding) {}
3133
3134 ScriptStreamingData::~ScriptStreamingData() = default;
3135
Release()3136 void ScriptStreamingData::Release() { task.reset(); }
3137
3138 } // namespace internal
3139 } // namespace v8
3140