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 #ifndef V8_CODEGEN_COMPILER_H_ 6 #define V8_CODEGEN_COMPILER_H_ 7 8 #include <forward_list> 9 #include <memory> 10 11 #include "src/base/platform/elapsed-timer.h" 12 #include "src/codegen/bailout-reason.h" 13 #include "src/common/globals.h" 14 #include "src/execution/isolate.h" 15 #include "src/execution/local-isolate.h" 16 #include "src/handles/persistent-handles.h" 17 #include "src/logging/code-events.h" 18 #include "src/objects/contexts.h" 19 #include "src/objects/debug-objects.h" 20 #include "src/parsing/parse-info.h" 21 #include "src/parsing/pending-compilation-error-handler.h" 22 #include "src/utils/allocation.h" 23 #include "src/zone/zone.h" 24 25 namespace v8 { 26 namespace internal { 27 28 // Forward declarations. 29 class AstRawString; 30 class BackgroundCompileTask; 31 class IsCompiledScope; 32 class JavaScriptFrame; 33 class OptimizedCompilationInfo; 34 class OptimizedCompilationJob; 35 class ParseInfo; 36 class Parser; 37 class RuntimeCallStats; 38 class ScriptData; 39 struct ScriptStreamingData; 40 class TimedHistogram; 41 class UnoptimizedCompilationInfo; 42 class UnoptimizedCompilationJob; 43 class WorkerThreadRuntimeCallStats; 44 45 using UnoptimizedCompilationJobList = 46 std::forward_list<std::unique_ptr<UnoptimizedCompilationJob>>; 47 48 // The V8 compiler API. 49 // 50 // This is the central hub for dispatching to the various compilers within V8. 51 // Logic for which compiler to choose and how to wire compilation results into 52 // the object heap should be kept inside this class. 53 // 54 // General strategy: Scripts are translated into anonymous functions w/o 55 // parameters which then can be executed. If the source code contains other 56 // functions, they might be compiled and allocated as part of the compilation 57 // of the source code or deferred for lazy compilation at a later point. 58 class V8_EXPORT_PRIVATE Compiler : public AllStatic { 59 public: 60 enum ClearExceptionFlag { KEEP_EXCEPTION, CLEAR_EXCEPTION }; 61 62 // =========================================================================== 63 // The following family of methods ensures a given function is compiled. The 64 // general contract is that failures will be reported by returning {false}, 65 // whereas successful compilation ensures the {is_compiled} predicate on the 66 // given function holds (except for live-edit, which compiles the world). 67 68 static bool Compile(Handle<SharedFunctionInfo> shared, 69 ClearExceptionFlag flag, 70 IsCompiledScope* is_compiled_scope); 71 static bool Compile(Handle<JSFunction> function, ClearExceptionFlag flag, 72 IsCompiledScope* is_compiled_scope); 73 static bool CompileOptimized(Handle<JSFunction> function, 74 ConcurrencyMode mode, CodeKind code_kind); 75 76 // Collect source positions for a function that has already been compiled to 77 // bytecode, but for which source positions were not collected (e.g. because 78 // they were not immediately needed). 79 static bool CollectSourcePositions(Isolate* isolate, 80 Handle<SharedFunctionInfo> shared); 81 82 V8_WARN_UNUSED_RESULT static MaybeHandle<SharedFunctionInfo> 83 CompileForLiveEdit(ParseInfo* parse_info, Handle<Script> script, 84 Isolate* isolate); 85 86 // Finalize and install code from previously run background compile task. 87 static bool FinalizeBackgroundCompileTask( 88 BackgroundCompileTask* task, Handle<SharedFunctionInfo> shared_info, 89 Isolate* isolate, ClearExceptionFlag flag); 90 91 // Finalize and install optimized code from previously run job. 92 static bool FinalizeOptimizedCompilationJob(OptimizedCompilationJob* job, 93 Isolate* isolate); 94 95 // Give the compiler a chance to perform low-latency initialization tasks of 96 // the given {function} on its instantiation. Note that only the runtime will 97 // offer this chance, optimized closure instantiation will not call this. 98 static void PostInstantiation(Handle<JSFunction> function); 99 100 // =========================================================================== 101 // The following family of methods instantiates new functions for scripts or 102 // function literals. The decision whether those functions will be compiled, 103 // is left to the discretion of the compiler. 104 // 105 // Please note this interface returns shared function infos. This means you 106 // need to call Factory::NewFunctionFromSharedFunctionInfo before you have a 107 // real function with a context. 108 109 // Create a (bound) function for a String source within a context for eval. 110 V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> GetFunctionFromEval( 111 Handle<String> source, Handle<SharedFunctionInfo> outer_info, 112 Handle<Context> context, LanguageMode language_mode, 113 ParseRestriction restriction, int parameters_end_pos, 114 int eval_scope_position, int eval_position); 115 116 struct ScriptDetails { ScriptDetailsScriptDetails117 ScriptDetails() 118 : line_offset(0), column_offset(0), repl_mode(REPLMode::kNo) {} ScriptDetailsScriptDetails119 explicit ScriptDetails(Handle<Object> script_name) 120 : line_offset(0), 121 column_offset(0), 122 name_obj(script_name), 123 repl_mode(REPLMode::kNo) {} 124 125 int line_offset; 126 int column_offset; 127 i::MaybeHandle<i::Object> name_obj; 128 i::MaybeHandle<i::Object> source_map_url; 129 i::MaybeHandle<i::FixedArray> host_defined_options; 130 REPLMode repl_mode; 131 }; 132 133 // Create a function that results from wrapping |source| in a function, 134 // with |arguments| being a list of parameters for that function. 135 V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> GetWrappedFunction( 136 Handle<String> source, Handle<FixedArray> arguments, 137 Handle<Context> context, const ScriptDetails& script_details, 138 ScriptOriginOptions origin_options, ScriptData* cached_data, 139 v8::ScriptCompiler::CompileOptions compile_options, 140 v8::ScriptCompiler::NoCacheReason no_cache_reason); 141 142 // Create a (bound) function for a String source within a context for eval. 143 V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> GetFunctionFromString( 144 Handle<Context> context, Handle<i::Object> source, 145 ParseRestriction restriction, int parameters_end_pos, bool is_code_like); 146 147 // Decompose GetFunctionFromString into two functions, to allow callers to 148 // deal seperately with a case of object not handled by the embedder. 149 V8_WARN_UNUSED_RESULT static std::pair<MaybeHandle<String>, bool> 150 ValidateDynamicCompilationSource(Isolate* isolate, Handle<Context> context, 151 Handle<i::Object> source_object, 152 bool is_code_like = false); 153 V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> 154 GetFunctionFromValidatedString(Handle<Context> context, 155 MaybeHandle<String> source, 156 ParseRestriction restriction, 157 int parameters_end_pos); 158 159 // Create a shared function info object for a String source. 160 static MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScript( 161 Isolate* isolate, Handle<String> source, 162 const ScriptDetails& script_details, ScriptOriginOptions origin_options, 163 v8::Extension* extension, ScriptData* cached_data, 164 ScriptCompiler::CompileOptions compile_options, 165 ScriptCompiler::NoCacheReason no_cache_reason, 166 NativesFlag is_natives_code); 167 168 // Create a shared function info object for a Script source that has already 169 // been parsed and possibly compiled on a background thread while being loaded 170 // from a streamed source. On return, the data held by |streaming_data| will 171 // have been released, however the object itself isn't freed and is still 172 // owned by the caller. 173 static MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForStreamedScript( 174 Isolate* isolate, Handle<String> source, 175 const ScriptDetails& script_details, ScriptOriginOptions origin_options, 176 ScriptStreamingData* streaming_data); 177 178 // Create a shared function info object for the given function literal 179 // node (the code may be lazily compiled). 180 template <typename LocalIsolate> 181 static Handle<SharedFunctionInfo> GetSharedFunctionInfo( 182 FunctionLiteral* node, Handle<Script> script, LocalIsolate* isolate); 183 184 // =========================================================================== 185 // The following family of methods provides support for OSR. Code generated 186 // for entry via OSR might not be suitable for normal entry, hence will be 187 // returned directly to the caller. 188 // 189 // Please note this interface is the only part dealing with {Code} objects 190 // directly. Other methods are agnostic to {Code} and can use an interpreter 191 // instead of generating JIT code for a function at all. 192 193 // Generate and return optimized code for OSR, or empty handle on failure. 194 V8_WARN_UNUSED_RESULT static MaybeHandle<Code> GetOptimizedCodeForOSR( 195 Handle<JSFunction> function, BailoutId osr_offset, 196 JavaScriptFrame* osr_frame); 197 }; 198 199 // A base class for compilation jobs intended to run concurrent to the main 200 // thread. The current state of the job can be checked using {state()}. 201 class V8_EXPORT_PRIVATE CompilationJob { 202 public: 203 enum Status { SUCCEEDED, FAILED, RETRY_ON_MAIN_THREAD }; 204 enum class State { 205 kReadyToPrepare, 206 kReadyToExecute, 207 kReadyToFinalize, 208 kSucceeded, 209 kFailed, 210 }; 211 CompilationJob(State initial_state)212 explicit CompilationJob(State initial_state) : state_(initial_state) { 213 timer_.Start(); 214 } 215 virtual ~CompilationJob() = default; 216 state()217 State state() const { return state_; } 218 219 protected: ElapsedTime()220 V8_WARN_UNUSED_RESULT base::TimeDelta ElapsedTime() const { 221 return timer_.Elapsed(); 222 } 223 UpdateState(Status status,State next_state)224 V8_WARN_UNUSED_RESULT Status UpdateState(Status status, State next_state) { 225 switch (status) { 226 case SUCCEEDED: 227 state_ = next_state; 228 break; 229 case FAILED: 230 state_ = State::kFailed; 231 break; 232 case RETRY_ON_MAIN_THREAD: 233 // Don't change the state, we'll re-try on the main thread. 234 break; 235 } 236 return status; 237 } 238 239 private: 240 State state_; 241 base::ElapsedTimer timer_; 242 }; 243 244 // A base class for unoptimized compilation jobs. 245 // 246 // The job is split into two phases which are called in sequence on 247 // different threads and with different limitations: 248 // 1) ExecuteJob: Runs concurrently. No heap allocation or handle derefs. 249 // 2) FinalizeJob: Runs on main thread. No dependency changes. 250 // 251 // Either of phases can either fail or succeed. 252 class UnoptimizedCompilationJob : public CompilationJob { 253 public: UnoptimizedCompilationJob(uintptr_t stack_limit,ParseInfo * parse_info,UnoptimizedCompilationInfo * compilation_info)254 UnoptimizedCompilationJob(uintptr_t stack_limit, ParseInfo* parse_info, 255 UnoptimizedCompilationInfo* compilation_info) 256 : CompilationJob(State::kReadyToExecute), 257 stack_limit_(stack_limit), 258 parse_info_(parse_info), 259 compilation_info_(compilation_info) {} 260 261 // Executes the compile job. Can be called on a background thread. 262 V8_WARN_UNUSED_RESULT Status ExecuteJob(); 263 264 // Finalizes the compile job. Must be called on the main thread. 265 V8_WARN_UNUSED_RESULT Status 266 FinalizeJob(Handle<SharedFunctionInfo> shared_info, Isolate* isolate); 267 268 // Finalizes the compile job. Can be called on a background thread, and might 269 // return RETRY_ON_MAIN_THREAD if the finalization can't be run on the 270 // background thread, and should instead be retried on the foreground thread. 271 V8_WARN_UNUSED_RESULT Status 272 FinalizeJob(Handle<SharedFunctionInfo> shared_info, LocalIsolate* isolate); 273 274 void RecordCompilationStats(Isolate* isolate) const; 275 void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag, 276 Handle<SharedFunctionInfo> shared, 277 Isolate* isolate) const; 278 parse_info()279 ParseInfo* parse_info() const { 280 DCHECK_NOT_NULL(parse_info_); 281 return parse_info_; 282 } compilation_info()283 UnoptimizedCompilationInfo* compilation_info() const { 284 return compilation_info_; 285 } 286 stack_limit()287 uintptr_t stack_limit() const { return stack_limit_; } 288 time_taken_to_execute()289 base::TimeDelta time_taken_to_execute() const { 290 return time_taken_to_execute_; 291 } time_taken_to_finalize()292 base::TimeDelta time_taken_to_finalize() const { 293 return time_taken_to_finalize_; 294 } 295 ClearParseInfo()296 void ClearParseInfo() { parse_info_ = nullptr; } 297 298 protected: 299 // Overridden by the actual implementation. 300 virtual Status ExecuteJobImpl() = 0; 301 virtual Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info, 302 Isolate* isolate) = 0; 303 virtual Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info, 304 LocalIsolate* isolate) = 0; 305 306 private: 307 uintptr_t stack_limit_; 308 ParseInfo* parse_info_; 309 UnoptimizedCompilationInfo* compilation_info_; 310 base::TimeDelta time_taken_to_execute_; 311 base::TimeDelta time_taken_to_finalize_; 312 }; 313 314 // A base class for optimized compilation jobs. 315 // 316 // The job is split into three phases which are called in sequence on 317 // different threads and with different limitations: 318 // 1) PrepareJob: Runs on main thread. No major limitations. 319 // 2) ExecuteJob: Runs concurrently. No heap allocation or handle derefs. 320 // 3) FinalizeJob: Runs on main thread. No dependency changes. 321 // 322 // Each of the three phases can either fail or succeed. 323 class OptimizedCompilationJob : public CompilationJob { 324 public: 325 OptimizedCompilationJob(OptimizedCompilationInfo* compilation_info, 326 const char* compiler_name, 327 State initial_state = State::kReadyToPrepare) CompilationJob(initial_state)328 : CompilationJob(initial_state), 329 compilation_info_(compilation_info), 330 compiler_name_(compiler_name) {} 331 332 // Prepare the compile job. Must be called on the main thread. 333 V8_WARN_UNUSED_RESULT Status PrepareJob(Isolate* isolate); 334 335 // Executes the compile job. Can be called on a background thread if 336 // can_execute_on_background_thread() returns true. 337 V8_WARN_UNUSED_RESULT Status 338 ExecuteJob(RuntimeCallStats* stats, LocalIsolate* local_isolate = nullptr); 339 340 // Finalizes the compile job. Must be called on the main thread. 341 V8_WARN_UNUSED_RESULT Status FinalizeJob(Isolate* isolate); 342 343 // Report a transient failure, try again next time. Should only be called on 344 // optimization compilation jobs. 345 Status RetryOptimization(BailoutReason reason); 346 347 // Report a persistent failure, disable future optimization on the function. 348 // Should only be called on optimization compilation jobs. 349 Status AbortOptimization(BailoutReason reason); 350 351 enum CompilationMode { kConcurrent, kSynchronous }; 352 void RecordCompilationStats(CompilationMode mode, Isolate* isolate) const; 353 void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag, 354 Isolate* isolate) const; 355 compilation_info()356 OptimizedCompilationInfo* compilation_info() const { 357 return compilation_info_; 358 } 359 360 protected: 361 // Overridden by the actual implementation. 362 virtual Status PrepareJobImpl(Isolate* isolate) = 0; 363 virtual Status ExecuteJobImpl(RuntimeCallStats* stats, 364 LocalIsolate* local_heap) = 0; 365 virtual Status FinalizeJobImpl(Isolate* isolate) = 0; 366 367 private: 368 OptimizedCompilationInfo* compilation_info_; 369 base::TimeDelta time_taken_to_prepare_; 370 base::TimeDelta time_taken_to_execute_; 371 base::TimeDelta time_taken_to_finalize_; 372 const char* compiler_name_; 373 }; 374 375 class FinalizeUnoptimizedCompilationData { 376 public: FinalizeUnoptimizedCompilationData(Isolate * isolate,Handle<SharedFunctionInfo> function_handle,MaybeHandle<CoverageInfo> coverage_info,base::TimeDelta time_taken_to_execute,base::TimeDelta time_taken_to_finalize)377 FinalizeUnoptimizedCompilationData(Isolate* isolate, 378 Handle<SharedFunctionInfo> function_handle, 379 MaybeHandle<CoverageInfo> coverage_info, 380 base::TimeDelta time_taken_to_execute, 381 base::TimeDelta time_taken_to_finalize) 382 : time_taken_to_execute_(time_taken_to_execute), 383 time_taken_to_finalize_(time_taken_to_finalize), 384 function_handle_(function_handle), 385 coverage_info_(coverage_info) {} 386 387 FinalizeUnoptimizedCompilationData(LocalIsolate* isolate, 388 Handle<SharedFunctionInfo> function_handle, 389 MaybeHandle<CoverageInfo> coverage_info, 390 base::TimeDelta time_taken_to_execute, 391 base::TimeDelta time_taken_to_finalize); 392 function_handle()393 Handle<SharedFunctionInfo> function_handle() const { 394 return function_handle_; 395 } 396 coverage_info()397 MaybeHandle<CoverageInfo> coverage_info() const { return coverage_info_; } 398 time_taken_to_execute()399 base::TimeDelta time_taken_to_execute() const { 400 return time_taken_to_execute_; 401 } time_taken_to_finalize()402 base::TimeDelta time_taken_to_finalize() const { 403 return time_taken_to_finalize_; 404 } 405 406 private: 407 base::TimeDelta time_taken_to_execute_; 408 base::TimeDelta time_taken_to_finalize_; 409 Handle<SharedFunctionInfo> function_handle_; 410 MaybeHandle<CoverageInfo> coverage_info_; 411 }; 412 413 using FinalizeUnoptimizedCompilationDataList = 414 std::vector<FinalizeUnoptimizedCompilationData>; 415 416 class DeferredFinalizationJobData { 417 public: DeferredFinalizationJobData(Isolate * isolate,Handle<SharedFunctionInfo> function_handle,std::unique_ptr<UnoptimizedCompilationJob> job)418 DeferredFinalizationJobData(Isolate* isolate, 419 Handle<SharedFunctionInfo> function_handle, 420 std::unique_ptr<UnoptimizedCompilationJob> job) { 421 UNREACHABLE(); 422 } 423 DeferredFinalizationJobData(LocalIsolate* isolate, 424 Handle<SharedFunctionInfo> function_handle, 425 std::unique_ptr<UnoptimizedCompilationJob> job); 426 function_handle()427 Handle<SharedFunctionInfo> function_handle() const { 428 return function_handle_; 429 } 430 job()431 UnoptimizedCompilationJob* job() const { return job_.get(); } 432 433 private: 434 Handle<SharedFunctionInfo> function_handle_; 435 std::unique_ptr<UnoptimizedCompilationJob> job_; 436 }; 437 438 // A wrapper around a OptimizedCompilationInfo that detaches the Handles from 439 // the underlying PersistentHandlesScope and stores them in info_ on 440 // destruction. 441 class CompilationHandleScope final { 442 public: CompilationHandleScope(Isolate * isolate,OptimizedCompilationInfo * info)443 explicit CompilationHandleScope(Isolate* isolate, 444 OptimizedCompilationInfo* info) 445 : persistent_(isolate), info_(info) {} 446 ~CompilationHandleScope(); 447 448 private: 449 PersistentHandlesScope persistent_; 450 OptimizedCompilationInfo* info_; 451 }; 452 453 using DeferredFinalizationJobDataList = 454 std::vector<DeferredFinalizationJobData>; 455 456 class V8_EXPORT_PRIVATE BackgroundCompileTask { 457 public: 458 // Creates a new task that when run will parse and compile the streamed 459 // script associated with |data| and can be finalized with 460 // Compiler::GetSharedFunctionInfoForStreamedScript. 461 // Note: does not take ownership of |data|. 462 BackgroundCompileTask(ScriptStreamingData* data, Isolate* isolate); 463 ~BackgroundCompileTask(); 464 465 // Creates a new task that when run will parse and compile the 466 // |function_literal| and can be finalized with 467 // Compiler::FinalizeBackgroundCompileTask. 468 BackgroundCompileTask( 469 const ParseInfo* outer_parse_info, const AstRawString* function_name, 470 const FunctionLiteral* function_literal, 471 WorkerThreadRuntimeCallStats* worker_thread_runtime_stats, 472 TimedHistogram* timer, int max_stack_size); 473 474 void Run(); 475 info()476 ParseInfo* info() { 477 DCHECK_NOT_NULL(info_); 478 return info_.get(); 479 } parser()480 Parser* parser() { return parser_.get(); } compilation_jobs()481 UnoptimizedCompilationJobList* compilation_jobs() { 482 return &compilation_jobs_; 483 } flags()484 UnoptimizedCompileFlags flags() const { return flags_; } compile_state()485 UnoptimizedCompileState* compile_state() { return &compile_state_; } language_mode()486 LanguageMode language_mode() { return language_mode_; } 487 FinalizeUnoptimizedCompilationDataList* finalize_unoptimized_compilation_data()488 finalize_unoptimized_compilation_data() { 489 return &finalize_unoptimized_compilation_data_; 490 } 491 492 // Jobs which could not be finalized in the background task, and need to be 493 // finalized on the main thread. jobs_to_retry_finalization_on_main_thread()494 DeferredFinalizationJobDataList* jobs_to_retry_finalization_on_main_thread() { 495 return &jobs_to_retry_finalization_on_main_thread_; 496 } 497 498 // Getters for the off-thread finalization results, that create main-thread 499 // handles to the objects. 500 MaybeHandle<SharedFunctionInfo> GetOuterFunctionSfi(Isolate* isolate); 501 Handle<Script> GetScript(Isolate* isolate); 502 503 private: 504 // Data needed for parsing, and data needed to to be passed between thread 505 // between parsing and compilation. These need to be initialized before the 506 // compilation starts. 507 UnoptimizedCompileFlags flags_; 508 UnoptimizedCompileState compile_state_; 509 std::unique_ptr<ParseInfo> info_; 510 std::unique_ptr<Parser> parser_; 511 512 // Data needed for finalizing compilation after background compilation. 513 UnoptimizedCompilationJobList compilation_jobs_; 514 515 // Data needed for merging onto the main thread after background finalization. 516 // TODO(leszeks): When these are available, the above fields are not. We 517 // should add some stricter type-safety or DCHECKs to ensure that the user of 518 // the task knows this. 519 Isolate* isolate_for_local_isolate_; 520 std::unique_ptr<PersistentHandles> persistent_handles_; 521 MaybeHandle<SharedFunctionInfo> outer_function_sfi_; 522 Handle<Script> script_; 523 IsCompiledScope is_compiled_scope_; 524 FinalizeUnoptimizedCompilationDataList finalize_unoptimized_compilation_data_; 525 DeferredFinalizationJobDataList jobs_to_retry_finalization_on_main_thread_; 526 527 // Single function data for top-level function compilation. 528 int start_position_; 529 int end_position_; 530 int function_literal_id_; 531 532 int stack_size_; 533 WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_; 534 TimedHistogram* timer_; 535 LanguageMode language_mode_; 536 537 DISALLOW_COPY_AND_ASSIGN(BackgroundCompileTask); 538 }; 539 540 // Contains all data which needs to be transmitted between threads for 541 // background parsing and compiling and finalizing it on the main thread. 542 struct ScriptStreamingData { 543 ScriptStreamingData( 544 std::unique_ptr<ScriptCompiler::ExternalSourceStream> source_stream, 545 ScriptCompiler::StreamedSource::Encoding encoding); 546 ~ScriptStreamingData(); 547 548 void Release(); 549 550 // Internal implementation of v8::ScriptCompiler::StreamedSource. 551 std::unique_ptr<ScriptCompiler::ExternalSourceStream> source_stream; 552 ScriptCompiler::StreamedSource::Encoding encoding; 553 554 // Task that performs background parsing and compilation. 555 std::unique_ptr<BackgroundCompileTask> task; 556 557 DISALLOW_COPY_AND_ASSIGN(ScriptStreamingData); 558 }; 559 560 } // namespace internal 561 } // namespace v8 562 563 #endif // V8_CODEGEN_COMPILER_H_ 564