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