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 72namespace v8 { 73namespace internal { 74 75namespace { 76 77constexpr bool IsOSR(BytecodeOffset osr_offset) { return !osr_offset.IsNone(); } 78 79void 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 88void ResetTieringState(JSFunction function, BytecodeOffset osr_offset) { 89 if (function.has_feedback_vector()) { 90 SetTieringState(function, osr_offset, TieringState::kNone); 91 } 92} 93 94void 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 102class CompilerTracer : public AllStatic { 103 public: 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 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 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 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 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 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 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 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 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 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 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: 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 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 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 242 static void PrintTraceSuffix(const CodeTracer::Scope& scope) { 243 PrintF(scope.file(), "]\n"); 244 } 245}; 246 247void 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. 324struct ScopedTimer { 325 explicit ScopedTimer(base::TimeDelta* location) : location_(location) { 326 DCHECK_NOT_NULL(location_); 327 timer_.Start(); 328 } 329 330 ~ScopedTimer() { *location_ += timer_.Elapsed(); } 331 332 base::ElapsedTimer timer_; 333 base::TimeDelta* location_; 334}; 335 336namespace { 337 338ScriptOriginOptions 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 360CompilationJob::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 367CompilationJob::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 379CompilationJob::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 387namespace { 388 389void 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 410void 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 442CompilationJob::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 452CompilationJob::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 462CompilationJob::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 472CompilationJob::Status TurbofanCompilationJob::RetryOptimization( 473 BailoutReason reason) { 474 DCHECK(compilation_info_->IsOptimizing()); 475 compilation_info_->RetryOptimization(reason); 476 return UpdateState(FAILED, State::kFailed); 477} 478 479CompilationJob::Status TurbofanCompilationJob::AbortOptimization( 480 BailoutReason reason) { 481 DCHECK(compilation_info_->IsOptimizing()); 482 compilation_info_->AbortOptimization(reason); 483 return UpdateState(FAILED, State::kFailed); 484} 485 486void 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 556void 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 577namespace { 578 579#if V8_ENABLE_WEBASSEMBLY 580bool 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 596void 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 633template <typename IsolateT> 634void 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 669void 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 680template <typename IsolateT> 681void 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 697void 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. 720template <typename IsolateT> 721CompilationJob::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 747std::unique_ptr<UnoptimizedCompilationJob> 748ExecuteSingleUnoptimizedCompilationJob( 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 780template <typename IsolateT> 781bool 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 861bool 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. 895class OptimizedCodeCache : public AllStatic { 896 public: 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 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. 972bool 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 982bool 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 1021bool 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 1070enum 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 1077bool 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 1090MaybeHandle<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. 1133void 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 1150MaybeHandle<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 1213MaybeHandle<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. 1274void 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 1293bool FailAndClearPendingException(Isolate* isolate) { 1294 isolate->clear_pending_exception(); 1295 return false; 1296} 1297 1298template <typename IsolateT> 1299bool 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 1307bool 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 1325bool 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 1333void 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 1381void 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 1397void 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. 1413template <typename IsolateT> 1414Handle<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 1423MaybeHandle<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 1486RuntimeCallCounterId 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 1499CompilationHandleScope::~CompilationHandleScope() { 1500 info_->set_persistent_handles(persistent_.Detach()); 1501} 1502 1503FinalizeUnoptimizedCompilationData::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 1514DeferredFinalizationJobData::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 1520BackgroundCompileTask::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 1541BackgroundCompileTask::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 1569BackgroundCompileTask::~BackgroundCompileTask() = default; 1570 1571namespace { 1572 1573void 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 1602void 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 1613void 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 1619void 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 1729MaybeHandle<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 1781bool 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 1822void 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 1830void 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 1841BackgroundDeserializeTask::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 1854void 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 1864MaybeHandle<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 1876bool 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 1979bool 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 2060bool 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 2135bool 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 2187bool 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 2206bool 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 2232MaybeHandle<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 2240bool 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 2257void 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 2287MaybeHandle<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 2295MaybeHandle<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) 2435bool 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) 2451bool 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 2492std::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 2554MaybeHandle<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 2582MaybeHandle<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 2593namespace { 2594 2595struct 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 2795Handle<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 2809MaybeHandle<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 2829class 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 2876bool 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 2888bool 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 2898MaybeHandle<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 2964MaybeHandle<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 3105MaybeHandle<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 3115MaybeHandle<SharedFunctionInfo> 3116Compiler::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 3125MaybeHandle<SharedFunctionInfo> 3126Compiler::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 3136MaybeHandle<SharedFunctionInfo> 3137Compiler::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 3149MaybeHandle<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 3244MaybeHandle<SharedFunctionInfo> 3245Compiler::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 3301Handle<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 3325template <typename IsolateT> 3326Handle<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 3370template Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( 3371 FunctionLiteral* literal, Handle<Script> script, Isolate* isolate); 3372template Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( 3373 FunctionLiteral* literal, Handle<Script> script, LocalIsolate* isolate); 3374 3375// static 3376MaybeHandle<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 3424void 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 3434bool 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 3499bool 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 3509void 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 3562ScriptStreamingData::ScriptStreamingData( 3563 std::unique_ptr<ScriptCompiler::ExternalSourceStream> source_stream, 3564 ScriptCompiler::StreamedSource::Encoding encoding) 3565 : source_stream(std::move(source_stream)), encoding(encoding) {} 3566 3567ScriptStreamingData::~ScriptStreamingData() = default; 3568 3569void ScriptStreamingData::Release() { task.reset(); } 3570 3571} // namespace internal 3572} // namespace v8 3573