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/compiler.h" |
6 | |
7 | #include <algorithm> |
8 | #include <memory> |
9 | |
10 | #include "src/api-inl.h" |
11 | #include "src/asmjs/asm-js.h" |
12 | #include "src/assembler-inl.h" |
13 | #include "src/ast/prettyprinter.h" |
14 | #include "src/ast/scopes.h" |
15 | #include "src/base/optional.h" |
16 | #include "src/bootstrapper.h" |
17 | #include "src/compilation-cache.h" |
18 | #include "src/compiler-dispatcher/compiler-dispatcher.h" |
19 | #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" |
20 | #include "src/compiler/pipeline.h" |
21 | #include "src/debug/debug.h" |
22 | #include "src/debug/liveedit.h" |
23 | #include "src/frames-inl.h" |
24 | #include "src/globals.h" |
25 | #include "src/heap/heap-inl.h" |
26 | #include "src/interpreter/interpreter.h" |
27 | #include "src/isolate-inl.h" |
28 | #include "src/log-inl.h" |
29 | #include "src/message-template.h" |
30 | #include "src/objects/feedback-cell-inl.h" |
31 | #include "src/objects/map.h" |
32 | #include "src/optimized-compilation-info.h" |
33 | #include "src/ostreams.h" |
34 | #include "src/parsing/parse-info.h" |
35 | #include "src/parsing/parser.h" |
36 | #include "src/parsing/parsing.h" |
37 | #include "src/parsing/rewriter.h" |
38 | #include "src/parsing/scanner-character-streams.h" |
39 | #include "src/runtime-profiler.h" |
40 | #include "src/snapshot/code-serializer.h" |
41 | #include "src/unoptimized-compilation-info.h" |
42 | #include "src/vm-state-inl.h" |
43 | #include "src/zone/zone-list-inl.h" // crbug.com/v8/8816 |
44 | |
45 | namespace v8 { |
46 | namespace internal { |
47 | |
48 | // A wrapper around a OptimizedCompilationInfo that detaches the Handles from |
49 | // the underlying DeferredHandleScope and stores them in info_ on |
50 | // destruction. |
51 | class CompilationHandleScope final { |
52 | public: |
53 | explicit CompilationHandleScope(Isolate* isolate, |
54 | OptimizedCompilationInfo* info) |
55 | : deferred_(isolate), info_(info) {} |
56 | ~CompilationHandleScope() { info_->set_deferred_handles(deferred_.Detach()); } |
57 | |
58 | private: |
59 | DeferredHandleScope deferred_; |
60 | OptimizedCompilationInfo* info_; |
61 | }; |
62 | |
63 | // Helper that times a scoped region and records the elapsed time. |
64 | struct ScopedTimer { |
65 | explicit ScopedTimer(base::TimeDelta* location) : location_(location) { |
66 | DCHECK_NOT_NULL(location_); |
67 | timer_.Start(); |
68 | } |
69 | |
70 | ~ScopedTimer() { *location_ += timer_.Elapsed(); } |
71 | |
72 | base::ElapsedTimer timer_; |
73 | base::TimeDelta* location_; |
74 | }; |
75 | |
76 | namespace { |
77 | |
78 | void LogFunctionCompilation(CodeEventListener::LogEventsAndTags tag, |
79 | Handle<SharedFunctionInfo> shared, |
80 | Handle<Script> script, |
81 | Handle<AbstractCode> abstract_code, bool optimizing, |
82 | double time_taken_ms, Isolate* isolate) { |
83 | DCHECK(!abstract_code.is_null()); |
84 | DCHECK(!abstract_code.is_identical_to(BUILTIN_CODE(isolate, CompileLazy))); |
85 | |
86 | // Log the code generation. If source information is available include |
87 | // script name and line number. Check explicitly whether logging is |
88 | // enabled as finding the line number is not free. |
89 | if (!isolate->logger()->is_listening_to_code_events() && |
90 | !isolate->is_profiling() && !FLAG_log_function_events && |
91 | !isolate->code_event_dispatcher()->IsListeningToCodeEvents()) { |
92 | return; |
93 | } |
94 | |
95 | int line_num = Script::GetLineNumber(script, shared->StartPosition()) + 1; |
96 | int column_num = Script::GetColumnNumber(script, shared->StartPosition()) + 1; |
97 | String script_name = script->name()->IsString() |
98 | ? String::cast(script->name()) |
99 | : ReadOnlyRoots(isolate).empty_string(); |
100 | CodeEventListener::LogEventsAndTags log_tag = |
101 | Logger::ToNativeByScript(tag, *script); |
102 | PROFILE(isolate, CodeCreateEvent(log_tag, *abstract_code, *shared, |
103 | script_name, line_num, column_num)); |
104 | if (!FLAG_log_function_events) return; |
105 | |
106 | DisallowHeapAllocation no_gc; |
107 | |
108 | std::string name = optimizing ? "optimize" : "compile" ; |
109 | switch (tag) { |
110 | case CodeEventListener::EVAL_TAG: |
111 | name += "-eval" ; |
112 | break; |
113 | case CodeEventListener::SCRIPT_TAG: |
114 | break; |
115 | case CodeEventListener::LAZY_COMPILE_TAG: |
116 | name += "-lazy" ; |
117 | break; |
118 | case CodeEventListener::FUNCTION_TAG: |
119 | break; |
120 | default: |
121 | UNREACHABLE(); |
122 | } |
123 | |
124 | LOG(isolate, FunctionEvent(name.c_str(), script->id(), time_taken_ms, |
125 | shared->StartPosition(), shared->EndPosition(), |
126 | shared->DebugName())); |
127 | } |
128 | |
129 | ScriptOriginOptions OriginOptionsForEval(Object script) { |
130 | if (!script->IsScript()) return ScriptOriginOptions(); |
131 | |
132 | const auto outer_origin_options = Script::cast(script)->origin_options(); |
133 | return ScriptOriginOptions(outer_origin_options.IsSharedCrossOrigin(), |
134 | outer_origin_options.IsOpaque()); |
135 | } |
136 | |
137 | } // namespace |
138 | |
139 | // ---------------------------------------------------------------------------- |
140 | // Implementation of UnoptimizedCompilationJob |
141 | |
142 | CompilationJob::Status UnoptimizedCompilationJob::ExecuteJob() { |
143 | DisallowHeapAccess no_heap_access; |
144 | // Delegate to the underlying implementation. |
145 | DCHECK_EQ(state(), State::kReadyToExecute); |
146 | ScopedTimer t(&time_taken_to_execute_); |
147 | return UpdateState(ExecuteJobImpl(), State::kReadyToFinalize); |
148 | } |
149 | |
150 | CompilationJob::Status UnoptimizedCompilationJob::FinalizeJob( |
151 | Handle<SharedFunctionInfo> shared_info, Isolate* isolate) { |
152 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
153 | DisallowCodeDependencyChange no_dependency_change; |
154 | DisallowJavascriptExecution no_js(isolate); |
155 | |
156 | // Delegate to the underlying implementation. |
157 | DCHECK_EQ(state(), State::kReadyToFinalize); |
158 | ScopedTimer t(&time_taken_to_finalize_); |
159 | return UpdateState(FinalizeJobImpl(shared_info, isolate), State::kSucceeded); |
160 | } |
161 | |
162 | void UnoptimizedCompilationJob::RecordCompilationStats(Isolate* isolate) const { |
163 | int code_size; |
164 | if (compilation_info()->has_bytecode_array()) { |
165 | code_size = compilation_info()->bytecode_array()->SizeIncludingMetadata(); |
166 | } else { |
167 | DCHECK(compilation_info()->has_asm_wasm_data()); |
168 | code_size = compilation_info()->asm_wasm_data()->Size(); |
169 | } |
170 | |
171 | Counters* counters = isolate->counters(); |
172 | // TODO(4280): Rename counters from "baseline" to "unoptimized" eventually. |
173 | counters->total_baseline_code_size()->Increment(code_size); |
174 | counters->total_baseline_compile_count()->Increment(1); |
175 | |
176 | // TODO(5203): Add timers for each phase of compilation. |
177 | // Also add total time (there's now already timer_ on the base class). |
178 | } |
179 | |
180 | void UnoptimizedCompilationJob::RecordFunctionCompilation( |
181 | CodeEventListener::LogEventsAndTags tag, Handle<SharedFunctionInfo> shared, |
182 | Isolate* isolate) const { |
183 | Handle<AbstractCode> abstract_code; |
184 | if (compilation_info()->has_bytecode_array()) { |
185 | abstract_code = |
186 | Handle<AbstractCode>::cast(compilation_info()->bytecode_array()); |
187 | } else { |
188 | DCHECK(compilation_info()->has_asm_wasm_data()); |
189 | abstract_code = |
190 | Handle<AbstractCode>::cast(BUILTIN_CODE(isolate, InstantiateAsmJs)); |
191 | } |
192 | |
193 | double time_taken_ms = time_taken_to_execute_.InMillisecondsF() + |
194 | time_taken_to_finalize_.InMillisecondsF(); |
195 | |
196 | LogFunctionCompilation(tag, shared, parse_info()->script(), abstract_code, |
197 | false, time_taken_ms, isolate); |
198 | } |
199 | |
200 | // ---------------------------------------------------------------------------- |
201 | // Implementation of OptimizedCompilationJob |
202 | |
203 | CompilationJob::Status OptimizedCompilationJob::PrepareJob(Isolate* isolate) { |
204 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
205 | DisallowJavascriptExecution no_js(isolate); |
206 | |
207 | if (FLAG_trace_opt && compilation_info()->IsOptimizing()) { |
208 | StdoutStream os; |
209 | os << "[compiling method " << Brief(*compilation_info()->closure()) |
210 | << " using " << compiler_name_; |
211 | if (compilation_info()->is_osr()) os << " OSR" ; |
212 | os << "]" << std::endl; |
213 | } |
214 | |
215 | // Delegate to the underlying implementation. |
216 | DCHECK_EQ(state(), State::kReadyToPrepare); |
217 | ScopedTimer t(&time_taken_to_prepare_); |
218 | return UpdateState(PrepareJobImpl(isolate), State::kReadyToExecute); |
219 | } |
220 | |
221 | CompilationJob::Status OptimizedCompilationJob::ExecuteJob() { |
222 | DisallowHeapAccess no_heap_access; |
223 | // Delegate to the underlying implementation. |
224 | DCHECK_EQ(state(), State::kReadyToExecute); |
225 | ScopedTimer t(&time_taken_to_execute_); |
226 | return UpdateState(ExecuteJobImpl(), State::kReadyToFinalize); |
227 | } |
228 | |
229 | CompilationJob::Status OptimizedCompilationJob::FinalizeJob(Isolate* isolate) { |
230 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
231 | DisallowJavascriptExecution no_js(isolate); |
232 | |
233 | // Delegate to the underlying implementation. |
234 | DCHECK_EQ(state(), State::kReadyToFinalize); |
235 | ScopedTimer t(&time_taken_to_finalize_); |
236 | return UpdateState(FinalizeJobImpl(isolate), State::kSucceeded); |
237 | } |
238 | |
239 | CompilationJob::Status OptimizedCompilationJob::RetryOptimization( |
240 | BailoutReason reason) { |
241 | DCHECK(compilation_info_->IsOptimizing()); |
242 | compilation_info_->RetryOptimization(reason); |
243 | return UpdateState(FAILED, State::kFailed); |
244 | } |
245 | |
246 | CompilationJob::Status OptimizedCompilationJob::AbortOptimization( |
247 | BailoutReason reason) { |
248 | DCHECK(compilation_info_->IsOptimizing()); |
249 | compilation_info_->AbortOptimization(reason); |
250 | return UpdateState(FAILED, State::kFailed); |
251 | } |
252 | |
253 | void OptimizedCompilationJob::RecordCompilationStats(CompilationMode mode, |
254 | Isolate* isolate) const { |
255 | DCHECK(compilation_info()->IsOptimizing()); |
256 | Handle<JSFunction> function = compilation_info()->closure(); |
257 | double ms_creategraph = time_taken_to_prepare_.InMillisecondsF(); |
258 | double ms_optimize = time_taken_to_execute_.InMillisecondsF(); |
259 | double ms_codegen = time_taken_to_finalize_.InMillisecondsF(); |
260 | if (FLAG_trace_opt) { |
261 | PrintF("[optimizing " ); |
262 | function->ShortPrint(); |
263 | PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n" , ms_creategraph, ms_optimize, |
264 | ms_codegen); |
265 | } |
266 | if (FLAG_trace_opt_stats) { |
267 | static double compilation_time = 0.0; |
268 | static int compiled_functions = 0; |
269 | static int code_size = 0; |
270 | |
271 | compilation_time += (ms_creategraph + ms_optimize + ms_codegen); |
272 | compiled_functions++; |
273 | code_size += function->shared()->SourceSize(); |
274 | PrintF("Compiled: %d functions with %d byte source size in %fms.\n" , |
275 | compiled_functions, code_size, compilation_time); |
276 | } |
277 | // Don't record samples from machines without high-resolution timers, |
278 | // as that can cause serious reporting issues. See the thread at |
279 | // http://g/chrome-metrics-team/NwwJEyL8odU/discussion for more details. |
280 | if (base::TimeTicks::IsHighResolution()) { |
281 | Counters* const counters = isolate->counters(); |
282 | if (compilation_info()->is_osr()) { |
283 | counters->turbofan_osr_prepare()->AddSample( |
284 | static_cast<int>(time_taken_to_prepare_.InMicroseconds())); |
285 | counters->turbofan_osr_execute()->AddSample( |
286 | static_cast<int>(time_taken_to_execute_.InMicroseconds())); |
287 | counters->turbofan_osr_finalize()->AddSample( |
288 | static_cast<int>(time_taken_to_finalize_.InMicroseconds())); |
289 | counters->turbofan_osr_total_time()->AddSample( |
290 | static_cast<int>(ElapsedTime().InMicroseconds())); |
291 | } else { |
292 | counters->turbofan_optimize_prepare()->AddSample( |
293 | static_cast<int>(time_taken_to_prepare_.InMicroseconds())); |
294 | counters->turbofan_optimize_execute()->AddSample( |
295 | static_cast<int>(time_taken_to_execute_.InMicroseconds())); |
296 | counters->turbofan_optimize_finalize()->AddSample( |
297 | static_cast<int>(time_taken_to_finalize_.InMicroseconds())); |
298 | counters->turbofan_optimize_total_time()->AddSample( |
299 | static_cast<int>(ElapsedTime().InMicroseconds())); |
300 | |
301 | // Compute foreground / background time. |
302 | base::TimeDelta time_background; |
303 | base::TimeDelta time_foreground = |
304 | time_taken_to_prepare_ + time_taken_to_finalize_; |
305 | switch (mode) { |
306 | case OptimizedCompilationJob::kConcurrent: |
307 | time_background += time_taken_to_execute_; |
308 | break; |
309 | case OptimizedCompilationJob::kSynchronous: |
310 | time_foreground += time_taken_to_execute_; |
311 | break; |
312 | } |
313 | counters->turbofan_optimize_total_background()->AddSample( |
314 | static_cast<int>(time_background.InMicroseconds())); |
315 | counters->turbofan_optimize_total_foreground()->AddSample( |
316 | static_cast<int>(time_foreground.InMicroseconds())); |
317 | } |
318 | } |
319 | } |
320 | |
321 | void OptimizedCompilationJob::RecordFunctionCompilation( |
322 | CodeEventListener::LogEventsAndTags tag, Isolate* isolate) const { |
323 | Handle<AbstractCode> abstract_code = |
324 | Handle<AbstractCode>::cast(compilation_info()->code()); |
325 | |
326 | double time_taken_ms = time_taken_to_prepare_.InMillisecondsF() + |
327 | time_taken_to_execute_.InMillisecondsF() + |
328 | time_taken_to_finalize_.InMillisecondsF(); |
329 | |
330 | Handle<Script> script( |
331 | Script::cast(compilation_info()->shared_info()->script()), isolate); |
332 | LogFunctionCompilation(tag, compilation_info()->shared_info(), script, |
333 | abstract_code, true, time_taken_ms, isolate); |
334 | } |
335 | |
336 | // ---------------------------------------------------------------------------- |
337 | // Local helper methods that make up the compilation pipeline. |
338 | |
339 | namespace { |
340 | |
341 | bool UseAsmWasm(FunctionLiteral* literal, bool asm_wasm_broken) { |
342 | // Check whether asm.js validation is enabled. |
343 | if (!FLAG_validate_asm) return false; |
344 | |
345 | // Modules that have validated successfully, but were subsequently broken by |
346 | // invalid module instantiation attempts are off limit forever. |
347 | if (asm_wasm_broken) return false; |
348 | |
349 | // In stress mode we want to run the validator on everything. |
350 | if (FLAG_stress_validate_asm) return true; |
351 | |
352 | // In general, we respect the "use asm" directive. |
353 | return literal->scope()->IsAsmModule(); |
354 | } |
355 | |
356 | void InstallBytecodeArray(Handle<BytecodeArray> bytecode_array, |
357 | Handle<SharedFunctionInfo> shared_info, |
358 | ParseInfo* parse_info, Isolate* isolate) { |
359 | if (!FLAG_interpreted_frames_native_stack) { |
360 | shared_info->set_bytecode_array(*bytecode_array); |
361 | return; |
362 | } |
363 | |
364 | Handle<Code> code = isolate->factory()->CopyCode(Handle<Code>::cast( |
365 | isolate->factory()->interpreter_entry_trampoline_for_profiling())); |
366 | |
367 | Handle<InterpreterData> interpreter_data = |
368 | Handle<InterpreterData>::cast(isolate->factory()->NewStruct( |
369 | INTERPRETER_DATA_TYPE, AllocationType::kOld)); |
370 | |
371 | interpreter_data->set_bytecode_array(*bytecode_array); |
372 | interpreter_data->set_interpreter_trampoline(*code); |
373 | |
374 | shared_info->set_interpreter_data(*interpreter_data); |
375 | |
376 | Handle<Script> script = parse_info->script(); |
377 | Handle<AbstractCode> abstract_code = Handle<AbstractCode>::cast(code); |
378 | int line_num = |
379 | Script::GetLineNumber(script, shared_info->StartPosition()) + 1; |
380 | int column_num = |
381 | Script::GetColumnNumber(script, shared_info->StartPosition()) + 1; |
382 | String script_name = script->name()->IsString() |
383 | ? String::cast(script->name()) |
384 | : ReadOnlyRoots(isolate).empty_string(); |
385 | CodeEventListener::LogEventsAndTags log_tag = Logger::ToNativeByScript( |
386 | CodeEventListener::INTERPRETED_FUNCTION_TAG, *script); |
387 | PROFILE(isolate, CodeCreateEvent(log_tag, *abstract_code, *shared_info, |
388 | script_name, line_num, column_num)); |
389 | } |
390 | |
391 | void InstallUnoptimizedCode(UnoptimizedCompilationInfo* compilation_info, |
392 | Handle<SharedFunctionInfo> shared_info, |
393 | ParseInfo* parse_info, Isolate* isolate) { |
394 | DCHECK_EQ(shared_info->language_mode(), |
395 | compilation_info->literal()->language_mode()); |
396 | |
397 | // Update the shared function info with the scope info. |
398 | Handle<ScopeInfo> scope_info = compilation_info->scope()->scope_info(); |
399 | shared_info->set_scope_info(*scope_info); |
400 | |
401 | if (compilation_info->has_bytecode_array()) { |
402 | DCHECK(!shared_info->HasBytecodeArray()); // Only compiled once. |
403 | DCHECK(!compilation_info->has_asm_wasm_data()); |
404 | DCHECK(!shared_info->HasFeedbackMetadata()); |
405 | |
406 | InstallBytecodeArray(compilation_info->bytecode_array(), shared_info, |
407 | parse_info, isolate); |
408 | |
409 | Handle<FeedbackMetadata> feedback_metadata = FeedbackMetadata::New( |
410 | isolate, compilation_info->feedback_vector_spec()); |
411 | shared_info->set_feedback_metadata(*feedback_metadata); |
412 | } else { |
413 | DCHECK(compilation_info->has_asm_wasm_data()); |
414 | shared_info->set_asm_wasm_data(*compilation_info->asm_wasm_data()); |
415 | shared_info->set_feedback_metadata( |
416 | ReadOnlyRoots(isolate).empty_feedback_metadata()); |
417 | } |
418 | |
419 | // Install coverage info on the shared function info. |
420 | if (compilation_info->has_coverage_info() && |
421 | !shared_info->HasCoverageInfo()) { |
422 | DCHECK(isolate->is_block_code_coverage()); |
423 | isolate->debug()->InstallCoverageInfo(shared_info, |
424 | compilation_info->coverage_info()); |
425 | } |
426 | } |
427 | |
428 | void EnsureSharedFunctionInfosArrayOnScript(ParseInfo* parse_info, |
429 | Isolate* isolate) { |
430 | DCHECK(parse_info->is_toplevel()); |
431 | DCHECK(!parse_info->script().is_null()); |
432 | if (parse_info->script()->shared_function_infos()->length() > 0) { |
433 | DCHECK_EQ(parse_info->script()->shared_function_infos()->length(), |
434 | parse_info->max_function_literal_id() + 1); |
435 | return; |
436 | } |
437 | Handle<WeakFixedArray> infos(isolate->factory()->NewWeakFixedArray( |
438 | parse_info->max_function_literal_id() + 1)); |
439 | parse_info->script()->set_shared_function_infos(*infos); |
440 | } |
441 | |
442 | void SetSharedFunctionFlagsFromLiteral(FunctionLiteral* literal, |
443 | Handle<SharedFunctionInfo> shared_info) { |
444 | shared_info->set_has_duplicate_parameters( |
445 | literal->has_duplicate_parameters()); |
446 | shared_info->set_is_oneshot_iife(literal->is_oneshot_iife()); |
447 | shared_info->SetExpectedNofPropertiesFromEstimate(literal); |
448 | if (literal->dont_optimize_reason() != BailoutReason::kNoReason) { |
449 | shared_info->DisableOptimization(literal->dont_optimize_reason()); |
450 | } |
451 | shared_info->set_is_safe_to_skip_arguments_adaptor( |
452 | literal->SafeToSkipArgumentsAdaptor()); |
453 | } |
454 | |
455 | CompilationJob::Status FinalizeUnoptimizedCompilationJob( |
456 | UnoptimizedCompilationJob* job, Handle<SharedFunctionInfo> shared_info, |
457 | Isolate* isolate) { |
458 | UnoptimizedCompilationInfo* compilation_info = job->compilation_info(); |
459 | ParseInfo* parse_info = job->parse_info(); |
460 | |
461 | SetSharedFunctionFlagsFromLiteral(compilation_info->literal(), shared_info); |
462 | |
463 | CompilationJob::Status status = job->FinalizeJob(shared_info, isolate); |
464 | if (status == CompilationJob::SUCCEEDED) { |
465 | InstallUnoptimizedCode(compilation_info, shared_info, parse_info, isolate); |
466 | CodeEventListener::LogEventsAndTags log_tag; |
467 | if (parse_info->is_toplevel()) { |
468 | log_tag = compilation_info->is_eval() ? CodeEventListener::EVAL_TAG |
469 | : CodeEventListener::SCRIPT_TAG; |
470 | } else { |
471 | log_tag = parse_info->lazy_compile() ? CodeEventListener::LAZY_COMPILE_TAG |
472 | : CodeEventListener::FUNCTION_TAG; |
473 | } |
474 | job->RecordFunctionCompilation(log_tag, shared_info, isolate); |
475 | job->RecordCompilationStats(isolate); |
476 | } |
477 | return status; |
478 | } |
479 | |
480 | std::unique_ptr<UnoptimizedCompilationJob> ExecuteUnoptimizedCompileJobs( |
481 | ParseInfo* parse_info, FunctionLiteral* literal, |
482 | AccountingAllocator* allocator, |
483 | UnoptimizedCompilationJobList* inner_function_jobs) { |
484 | if (UseAsmWasm(literal, parse_info->is_asm_wasm_broken())) { |
485 | std::unique_ptr<UnoptimizedCompilationJob> asm_job( |
486 | AsmJs::NewCompilationJob(parse_info, literal, allocator)); |
487 | if (asm_job->ExecuteJob() == CompilationJob::SUCCEEDED) { |
488 | return asm_job; |
489 | } |
490 | // asm.js validation failed, fall through to standard unoptimized compile. |
491 | // Note: we rely on the fact that AsmJs jobs have done all validation in the |
492 | // PrepareJob and ExecuteJob phases and can't fail in FinalizeJob with |
493 | // with a validation error or another error that could be solve by falling |
494 | // through to standard unoptimized compile. |
495 | } |
496 | std::vector<FunctionLiteral*> eager_inner_literals; |
497 | std::unique_ptr<UnoptimizedCompilationJob> job( |
498 | interpreter::Interpreter::NewCompilationJob( |
499 | parse_info, literal, allocator, &eager_inner_literals)); |
500 | |
501 | if (job->ExecuteJob() != CompilationJob::SUCCEEDED) { |
502 | // Compilation failed, return null. |
503 | return std::unique_ptr<UnoptimizedCompilationJob>(); |
504 | } |
505 | |
506 | // Recursively compile eager inner literals. |
507 | for (FunctionLiteral* inner_literal : eager_inner_literals) { |
508 | std::unique_ptr<UnoptimizedCompilationJob> inner_job( |
509 | ExecuteUnoptimizedCompileJobs(parse_info, inner_literal, allocator, |
510 | inner_function_jobs)); |
511 | // Compilation failed, return null. |
512 | if (!inner_job) return std::unique_ptr<UnoptimizedCompilationJob>(); |
513 | inner_function_jobs->emplace_front(std::move(inner_job)); |
514 | } |
515 | |
516 | return job; |
517 | } |
518 | |
519 | std::unique_ptr<UnoptimizedCompilationJob> GenerateUnoptimizedCode( |
520 | ParseInfo* parse_info, AccountingAllocator* allocator, |
521 | UnoptimizedCompilationJobList* inner_function_jobs) { |
522 | DisallowHeapAccess no_heap_access; |
523 | DCHECK(inner_function_jobs->empty()); |
524 | |
525 | if (!Compiler::Analyze(parse_info)) { |
526 | return std::unique_ptr<UnoptimizedCompilationJob>(); |
527 | } |
528 | |
529 | // Prepare and execute compilation of the outer-most function. |
530 | std::unique_ptr<UnoptimizedCompilationJob> outer_function_job( |
531 | ExecuteUnoptimizedCompileJobs(parse_info, parse_info->literal(), |
532 | allocator, inner_function_jobs)); |
533 | if (!outer_function_job) return std::unique_ptr<UnoptimizedCompilationJob>(); |
534 | |
535 | // Character stream shouldn't be used again. |
536 | parse_info->ResetCharacterStream(); |
537 | |
538 | return outer_function_job; |
539 | } |
540 | |
541 | MaybeHandle<SharedFunctionInfo> GenerateUnoptimizedCodeForToplevel( |
542 | Isolate* isolate, ParseInfo* parse_info, AccountingAllocator* allocator, |
543 | IsCompiledScope* is_compiled_scope) { |
544 | EnsureSharedFunctionInfosArrayOnScript(parse_info, isolate); |
545 | parse_info->ast_value_factory()->Internalize(isolate); |
546 | |
547 | if (!Compiler::Analyze(parse_info)) return MaybeHandle<SharedFunctionInfo>(); |
548 | DeclarationScope::AllocateScopeInfos(parse_info, isolate); |
549 | |
550 | // Prepare and execute compilation of the outer-most function. |
551 | // Create the SharedFunctionInfo and add it to the script's list. |
552 | Handle<Script> script = parse_info->script(); |
553 | Handle<SharedFunctionInfo> top_level = |
554 | isolate->factory()->NewSharedFunctionInfoForLiteral(parse_info->literal(), |
555 | script, true); |
556 | |
557 | std::vector<FunctionLiteral*> functions_to_compile; |
558 | functions_to_compile.push_back(parse_info->literal()); |
559 | |
560 | while (!functions_to_compile.empty()) { |
561 | FunctionLiteral* literal = functions_to_compile.back(); |
562 | functions_to_compile.pop_back(); |
563 | Handle<SharedFunctionInfo> shared_info = |
564 | Compiler::GetSharedFunctionInfo(literal, script, isolate); |
565 | if (shared_info->is_compiled()) continue; |
566 | if (UseAsmWasm(literal, parse_info->is_asm_wasm_broken())) { |
567 | std::unique_ptr<UnoptimizedCompilationJob> asm_job( |
568 | AsmJs::NewCompilationJob(parse_info, literal, allocator)); |
569 | if (asm_job->ExecuteJob() == CompilationJob::SUCCEEDED && |
570 | FinalizeUnoptimizedCompilationJob(asm_job.get(), shared_info, |
571 | isolate) == |
572 | CompilationJob::SUCCEEDED) { |
573 | continue; |
574 | } |
575 | // asm.js validation failed, fall through to standard unoptimized compile. |
576 | // Note: we rely on the fact that AsmJs jobs have done all validation in |
577 | // the PrepareJob and ExecuteJob phases and can't fail in FinalizeJob with |
578 | // with a validation error or another error that could be solve by falling |
579 | // through to standard unoptimized compile. |
580 | } |
581 | |
582 | std::unique_ptr<UnoptimizedCompilationJob> job( |
583 | interpreter::Interpreter::NewCompilationJob( |
584 | parse_info, literal, allocator, &functions_to_compile)); |
585 | |
586 | if (job->ExecuteJob() == CompilationJob::FAILED || |
587 | FinalizeUnoptimizedCompilationJob(job.get(), shared_info, isolate) == |
588 | CompilationJob::FAILED) { |
589 | return MaybeHandle<SharedFunctionInfo>(); |
590 | } |
591 | |
592 | if (shared_info.is_identical_to(top_level)) { |
593 | // Ensure that the top level function is retained. |
594 | *is_compiled_scope = shared_info->is_compiled_scope(); |
595 | DCHECK(is_compiled_scope->is_compiled()); |
596 | } |
597 | } |
598 | |
599 | // Character stream shouldn't be used again. |
600 | parse_info->ResetCharacterStream(); |
601 | |
602 | return top_level; |
603 | } |
604 | |
605 | bool FinalizeUnoptimizedCode( |
606 | ParseInfo* parse_info, Isolate* isolate, |
607 | Handle<SharedFunctionInfo> shared_info, |
608 | UnoptimizedCompilationJob* outer_function_job, |
609 | UnoptimizedCompilationJobList* inner_function_jobs) { |
610 | DCHECK(AllowCompilation::IsAllowed(isolate)); |
611 | |
612 | // TODO(rmcilroy): Clear native context in debug once AsmJS generates doesn't |
613 | // rely on accessing native context during finalization. |
614 | |
615 | // Allocate scope infos for the literal. |
616 | DeclarationScope::AllocateScopeInfos(parse_info, isolate); |
617 | |
618 | // Finalize the outer-most function's compilation job. |
619 | if (FinalizeUnoptimizedCompilationJob(outer_function_job, shared_info, |
620 | isolate) != CompilationJob::SUCCEEDED) { |
621 | return false; |
622 | } |
623 | |
624 | // Finalize the inner functions' compilation jobs. |
625 | for (auto&& inner_job : *inner_function_jobs) { |
626 | Handle<SharedFunctionInfo> inner_shared_info = |
627 | Compiler::GetSharedFunctionInfo( |
628 | inner_job->compilation_info()->literal(), parse_info->script(), |
629 | isolate); |
630 | // The inner function might be compiled already if compiling for debug. |
631 | if (inner_shared_info->is_compiled()) continue; |
632 | if (FinalizeUnoptimizedCompilationJob(inner_job.get(), inner_shared_info, |
633 | isolate) != |
634 | CompilationJob::SUCCEEDED) { |
635 | return false; |
636 | } |
637 | } |
638 | |
639 | // Report any warnings generated during compilation. |
640 | if (parse_info->pending_error_handler()->has_pending_warnings()) { |
641 | parse_info->pending_error_handler()->ReportWarnings(isolate, |
642 | parse_info->script()); |
643 | } |
644 | |
645 | return true; |
646 | } |
647 | |
648 | V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache( |
649 | Handle<JSFunction> function, BailoutId osr_offset) { |
650 | RuntimeCallTimerScope runtimeTimer( |
651 | function->GetIsolate(), |
652 | RuntimeCallCounterId::kCompileGetFromOptimizedCodeMap); |
653 | Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate()); |
654 | DisallowHeapAllocation no_gc; |
655 | if (osr_offset.IsNone()) { |
656 | if (function->has_feedback_vector()) { |
657 | FeedbackVector feedback_vector = function->feedback_vector(); |
658 | feedback_vector->EvictOptimizedCodeMarkedForDeoptimization( |
659 | function->shared(), "GetCodeFromOptimizedCodeCache" ); |
660 | Code code = feedback_vector->optimized_code(); |
661 | |
662 | if (!code.is_null()) { |
663 | // Caching of optimized code enabled and optimized code found. |
664 | DCHECK(!code->marked_for_deoptimization()); |
665 | DCHECK(function->shared()->is_compiled()); |
666 | return Handle<Code>(code, feedback_vector->GetIsolate()); |
667 | } |
668 | } |
669 | } |
670 | return MaybeHandle<Code>(); |
671 | } |
672 | |
673 | void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) { |
674 | Handle<JSFunction> function = compilation_info->closure(); |
675 | if (compilation_info->osr_offset().IsNone()) { |
676 | Handle<FeedbackVector> vector = |
677 | handle(function->feedback_vector(), function->GetIsolate()); |
678 | vector->ClearOptimizationMarker(); |
679 | } |
680 | } |
681 | |
682 | void InsertCodeIntoOptimizedCodeCache( |
683 | OptimizedCompilationInfo* compilation_info) { |
684 | Handle<Code> code = compilation_info->code(); |
685 | if (code->kind() != Code::OPTIMIZED_FUNCTION) return; // Nothing to do. |
686 | |
687 | // Function context specialization folds-in the function context, |
688 | // so no sharing can occur. |
689 | if (compilation_info->is_function_context_specializing()) { |
690 | // Native context specialized code is not shared, so make sure the optimized |
691 | // code cache is clear. |
692 | ClearOptimizedCodeCache(compilation_info); |
693 | return; |
694 | } |
695 | |
696 | // Cache optimized context-specific code. |
697 | Handle<JSFunction> function = compilation_info->closure(); |
698 | Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate()); |
699 | Handle<Context> native_context(function->context()->native_context(), |
700 | function->GetIsolate()); |
701 | if (compilation_info->osr_offset().IsNone()) { |
702 | Handle<FeedbackVector> vector = |
703 | handle(function->feedback_vector(), function->GetIsolate()); |
704 | FeedbackVector::SetOptimizedCode(vector, code); |
705 | } |
706 | } |
707 | |
708 | bool GetOptimizedCodeNow(OptimizedCompilationJob* job, Isolate* isolate) { |
709 | TimerEventScope<TimerEventRecompileSynchronous> timer(isolate); |
710 | RuntimeCallTimerScope runtimeTimer( |
711 | isolate, RuntimeCallCounterId::kRecompileSynchronous); |
712 | OptimizedCompilationInfo* compilation_info = job->compilation_info(); |
713 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
714 | "V8.RecompileSynchronous" ); |
715 | |
716 | if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED || |
717 | job->ExecuteJob() != CompilationJob::SUCCEEDED || |
718 | job->FinalizeJob(isolate) != CompilationJob::SUCCEEDED) { |
719 | if (FLAG_trace_opt) { |
720 | PrintF("[aborted optimizing " ); |
721 | compilation_info->closure()->ShortPrint(); |
722 | PrintF(" because: %s]\n" , |
723 | GetBailoutReason(compilation_info->bailout_reason())); |
724 | } |
725 | return false; |
726 | } |
727 | |
728 | // Success! |
729 | job->RecordCompilationStats(OptimizedCompilationJob::kSynchronous, isolate); |
730 | DCHECK(!isolate->has_pending_exception()); |
731 | InsertCodeIntoOptimizedCodeCache(compilation_info); |
732 | job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, isolate); |
733 | return true; |
734 | } |
735 | |
736 | bool GetOptimizedCodeLater(OptimizedCompilationJob* job, Isolate* isolate) { |
737 | OptimizedCompilationInfo* compilation_info = job->compilation_info(); |
738 | if (!isolate->optimizing_compile_dispatcher()->IsQueueAvailable()) { |
739 | if (FLAG_trace_concurrent_recompilation) { |
740 | PrintF(" ** Compilation queue full, will retry optimizing " ); |
741 | compilation_info->closure()->ShortPrint(); |
742 | PrintF(" later.\n" ); |
743 | } |
744 | return false; |
745 | } |
746 | |
747 | if (isolate->heap()->HighMemoryPressure()) { |
748 | if (FLAG_trace_concurrent_recompilation) { |
749 | PrintF(" ** High memory pressure, will retry optimizing " ); |
750 | compilation_info->closure()->ShortPrint(); |
751 | PrintF(" later.\n" ); |
752 | } |
753 | return false; |
754 | } |
755 | |
756 | TimerEventScope<TimerEventRecompileSynchronous> timer(isolate); |
757 | RuntimeCallTimerScope runtimeTimer( |
758 | isolate, RuntimeCallCounterId::kRecompileSynchronous); |
759 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
760 | "V8.RecompileSynchronous" ); |
761 | |
762 | if (job->PrepareJob(isolate) != CompilationJob::SUCCEEDED) return false; |
763 | isolate->optimizing_compile_dispatcher()->QueueForOptimization(job); |
764 | |
765 | if (FLAG_trace_concurrent_recompilation) { |
766 | PrintF(" ** Queued " ); |
767 | compilation_info->closure()->ShortPrint(); |
768 | PrintF(" for concurrent optimization.\n" ); |
769 | } |
770 | return true; |
771 | } |
772 | |
773 | MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function, |
774 | ConcurrencyMode mode, |
775 | BailoutId osr_offset = BailoutId::None(), |
776 | JavaScriptFrame* osr_frame = nullptr) { |
777 | Isolate* isolate = function->GetIsolate(); |
778 | Handle<SharedFunctionInfo> shared(function->shared(), isolate); |
779 | |
780 | // Make sure we clear the optimization marker on the function so that we |
781 | // don't try to re-optimize. |
782 | if (function->HasOptimizationMarker()) { |
783 | function->ClearOptimizationMarker(); |
784 | } |
785 | |
786 | if (shared->optimization_disabled() && |
787 | shared->disable_optimization_reason() == BailoutReason::kNeverOptimize) { |
788 | return MaybeHandle<Code>(); |
789 | } |
790 | |
791 | if (isolate->debug()->needs_check_on_function_call()) { |
792 | // Do not optimize when debugger needs to hook into every call. |
793 | return MaybeHandle<Code>(); |
794 | } |
795 | |
796 | // If code was pending optimization for testing, delete remove the strong root |
797 | // that was preventing the bytecode from being flushed between marking and |
798 | // optimization. |
799 | if (!isolate->heap()->pending_optimize_for_test_bytecode()->IsUndefined()) { |
800 | Handle<ObjectHashTable> table = |
801 | handle(ObjectHashTable::cast( |
802 | isolate->heap()->pending_optimize_for_test_bytecode()), |
803 | isolate); |
804 | bool was_present; |
805 | table = table->Remove(isolate, table, handle(function->shared(), isolate), |
806 | &was_present); |
807 | isolate->heap()->SetPendingOptimizeForTestBytecode(*table); |
808 | } |
809 | |
810 | Handle<Code> cached_code; |
811 | if (GetCodeFromOptimizedCodeCache(function, osr_offset) |
812 | .ToHandle(&cached_code)) { |
813 | if (FLAG_trace_opt) { |
814 | PrintF("[found optimized code for " ); |
815 | function->ShortPrint(); |
816 | if (!osr_offset.IsNone()) { |
817 | PrintF(" at OSR AST id %d" , osr_offset.ToInt()); |
818 | } |
819 | PrintF("]\n" ); |
820 | } |
821 | return cached_code; |
822 | } |
823 | |
824 | // Reset profiler ticks, function is no longer considered hot. |
825 | DCHECK(shared->is_compiled()); |
826 | function->feedback_vector()->set_profiler_ticks(0); |
827 | |
828 | VMState<COMPILER> state(isolate); |
829 | TimerEventScope<TimerEventOptimizeCode> optimize_code_timer(isolate); |
830 | RuntimeCallTimerScope runtimeTimer(isolate, |
831 | RuntimeCallCounterId::kOptimizeCode); |
832 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), "V8.OptimizeCode" ); |
833 | |
834 | DCHECK(!isolate->has_pending_exception()); |
835 | PostponeInterruptsScope postpone(isolate); |
836 | bool has_script = shared->script()->IsScript(); |
837 | // BUG(5946): This DCHECK is necessary to make certain that we won't |
838 | // tolerate the lack of a script without bytecode. |
839 | DCHECK_IMPLIES(!has_script, shared->HasBytecodeArray()); |
840 | std::unique_ptr<OptimizedCompilationJob> job( |
841 | compiler::Pipeline::NewCompilationJob(isolate, function, has_script)); |
842 | OptimizedCompilationInfo* compilation_info = job->compilation_info(); |
843 | |
844 | compilation_info->SetOptimizingForOsr(osr_offset, osr_frame); |
845 | |
846 | // Do not use TurboFan if we need to be able to set break points. |
847 | if (compilation_info->shared_info()->HasBreakInfo()) { |
848 | compilation_info->AbortOptimization(BailoutReason::kFunctionBeingDebugged); |
849 | return MaybeHandle<Code>(); |
850 | } |
851 | |
852 | // Do not use TurboFan if optimization is disabled or function doesn't pass |
853 | // turbo_filter. |
854 | if (!FLAG_opt || !shared->PassesFilter(FLAG_turbo_filter)) { |
855 | compilation_info->AbortOptimization(BailoutReason::kOptimizationDisabled); |
856 | return MaybeHandle<Code>(); |
857 | } |
858 | |
859 | // In case of concurrent recompilation, all handles below this point will be |
860 | // allocated in a deferred handle scope that is detached and handed off to |
861 | // the background thread when we return. |
862 | base::Optional<CompilationHandleScope> compilation; |
863 | if (mode == ConcurrencyMode::kConcurrent) { |
864 | compilation.emplace(isolate, compilation_info); |
865 | } |
866 | |
867 | // All handles below will be canonicalized. |
868 | CanonicalHandleScope canonical(isolate); |
869 | |
870 | // Reopen handles in the new CompilationHandleScope. |
871 | compilation_info->ReopenHandlesInNewHandleScope(isolate); |
872 | |
873 | if (mode == ConcurrencyMode::kConcurrent) { |
874 | if (GetOptimizedCodeLater(job.get(), isolate)) { |
875 | job.release(); // The background recompile job owns this now. |
876 | |
877 | // Set the optimization marker and return a code object which checks it. |
878 | function->SetOptimizationMarker(OptimizationMarker::kInOptimizationQueue); |
879 | DCHECK(function->IsInterpreted() || |
880 | (!function->is_compiled() && function->shared()->IsInterpreted())); |
881 | DCHECK(function->shared()->HasBytecodeArray()); |
882 | return BUILTIN_CODE(isolate, InterpreterEntryTrampoline); |
883 | } |
884 | } else { |
885 | if (GetOptimizedCodeNow(job.get(), isolate)) |
886 | return compilation_info->code(); |
887 | } |
888 | |
889 | if (isolate->has_pending_exception()) isolate->clear_pending_exception(); |
890 | return MaybeHandle<Code>(); |
891 | } |
892 | |
893 | bool FailWithPendingException(Isolate* isolate, ParseInfo* parse_info, |
894 | Compiler::ClearExceptionFlag flag) { |
895 | if (flag == Compiler::CLEAR_EXCEPTION) { |
896 | isolate->clear_pending_exception(); |
897 | } else if (!isolate->has_pending_exception()) { |
898 | if (parse_info->pending_error_handler()->has_pending_error()) { |
899 | parse_info->pending_error_handler()->ReportErrors( |
900 | isolate, parse_info->script(), parse_info->ast_value_factory()); |
901 | } else { |
902 | isolate->StackOverflow(); |
903 | } |
904 | } |
905 | return false; |
906 | } |
907 | |
908 | void FinalizeScriptCompilation(Isolate* isolate, ParseInfo* parse_info) { |
909 | Handle<Script> script = parse_info->script(); |
910 | script->set_compilation_state(Script::COMPILATION_STATE_COMPILED); |
911 | |
912 | // Register any pending parallel tasks with the associated SFI. |
913 | if (parse_info->parallel_tasks()) { |
914 | CompilerDispatcher* dispatcher = parse_info->parallel_tasks()->dispatcher(); |
915 | for (auto& it : *parse_info->parallel_tasks()) { |
916 | FunctionLiteral* literal = it.first; |
917 | CompilerDispatcher::JobId job_id = it.second; |
918 | MaybeHandle<SharedFunctionInfo> maybe_shared_for_task = |
919 | script->FindSharedFunctionInfo(isolate, literal); |
920 | Handle<SharedFunctionInfo> shared_for_task; |
921 | if (maybe_shared_for_task.ToHandle(&shared_for_task)) { |
922 | dispatcher->RegisterSharedFunctionInfo(job_id, *shared_for_task); |
923 | } else { |
924 | dispatcher->AbortJob(job_id); |
925 | } |
926 | } |
927 | } |
928 | } |
929 | |
930 | MaybeHandle<SharedFunctionInfo> FinalizeTopLevel( |
931 | ParseInfo* parse_info, Isolate* isolate, |
932 | UnoptimizedCompilationJob* outer_function_job, |
933 | UnoptimizedCompilationJobList* inner_function_jobs) { |
934 | // Internalize ast values onto the heap. |
935 | parse_info->ast_value_factory()->Internalize(isolate); |
936 | |
937 | // Create shared function infos for top level and shared function infos array |
938 | // for inner functions. |
939 | EnsureSharedFunctionInfosArrayOnScript(parse_info, isolate); |
940 | DCHECK_EQ(kNoSourcePosition, |
941 | parse_info->literal()->function_token_position()); |
942 | Handle<SharedFunctionInfo> shared_info = |
943 | isolate->factory()->NewSharedFunctionInfoForLiteral( |
944 | parse_info->literal(), parse_info->script(), true); |
945 | |
946 | // Finalize compilation of the unoptimized bytecode or asm-js data. |
947 | if (!FinalizeUnoptimizedCode(parse_info, isolate, shared_info, |
948 | outer_function_job, inner_function_jobs)) { |
949 | FailWithPendingException(isolate, parse_info, |
950 | Compiler::ClearExceptionFlag::KEEP_EXCEPTION); |
951 | return MaybeHandle<SharedFunctionInfo>(); |
952 | } |
953 | |
954 | FinalizeScriptCompilation(isolate, parse_info); |
955 | |
956 | return shared_info; |
957 | } |
958 | |
959 | MaybeHandle<SharedFunctionInfo> CompileToplevel( |
960 | ParseInfo* parse_info, Isolate* isolate, |
961 | IsCompiledScope* is_compiled_scope) { |
962 | TimerEventScope<TimerEventCompileCode> top_level_timer(isolate); |
963 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), "V8.CompileCode" ); |
964 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
965 | |
966 | PostponeInterruptsScope postpone(isolate); |
967 | DCHECK(!isolate->native_context().is_null()); |
968 | RuntimeCallTimerScope runtimeTimer( |
969 | isolate, parse_info->is_eval() ? RuntimeCallCounterId::kCompileEval |
970 | : RuntimeCallCounterId::kCompileScript); |
971 | VMState<BYTECODE_COMPILER> state(isolate); |
972 | if (parse_info->literal() == nullptr && |
973 | !parsing::ParseProgram(parse_info, isolate)) { |
974 | return MaybeHandle<SharedFunctionInfo>(); |
975 | } |
976 | // Measure how long it takes to do the compilation; only take the |
977 | // rest of the function into account to avoid overlap with the |
978 | // parsing statistics. |
979 | HistogramTimer* rate = parse_info->is_eval() |
980 | ? isolate->counters()->compile_eval() |
981 | : isolate->counters()->compile(); |
982 | HistogramTimerScope timer(rate); |
983 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
984 | parse_info->is_eval() ? "V8.CompileEval" : "V8.Compile" ); |
985 | |
986 | // Generate the unoptimized bytecode or asm-js data. |
987 | MaybeHandle<SharedFunctionInfo> shared_info = |
988 | GenerateUnoptimizedCodeForToplevel( |
989 | isolate, parse_info, isolate->allocator(), is_compiled_scope); |
990 | if (shared_info.is_null()) { |
991 | FailWithPendingException(isolate, parse_info, |
992 | Compiler::ClearExceptionFlag::KEEP_EXCEPTION); |
993 | return MaybeHandle<SharedFunctionInfo>(); |
994 | } |
995 | |
996 | FinalizeScriptCompilation(isolate, parse_info); |
997 | return shared_info; |
998 | } |
999 | |
1000 | std::unique_ptr<UnoptimizedCompilationJob> CompileOnBackgroundThread( |
1001 | ParseInfo* parse_info, AccountingAllocator* allocator, |
1002 | UnoptimizedCompilationJobList* inner_function_jobs) { |
1003 | DisallowHeapAccess no_heap_access; |
1004 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
1005 | "V8.CompileCodeBackground" ); |
1006 | RuntimeCallTimerScope runtimeTimer( |
1007 | parse_info->runtime_call_stats(), |
1008 | parse_info->is_toplevel() |
1009 | ? parse_info->is_eval() |
1010 | ? RuntimeCallCounterId::kCompileBackgroundEval |
1011 | : RuntimeCallCounterId::kCompileBackgroundScript |
1012 | : RuntimeCallCounterId::kCompileBackgroundFunction); |
1013 | |
1014 | // Generate the unoptimized bytecode or asm-js data. |
1015 | std::unique_ptr<UnoptimizedCompilationJob> outer_function_job( |
1016 | GenerateUnoptimizedCode(parse_info, allocator, inner_function_jobs)); |
1017 | return outer_function_job; |
1018 | } |
1019 | |
1020 | } // namespace |
1021 | |
1022 | BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* streamed_data, |
1023 | Isolate* isolate) |
1024 | : info_(new ParseInfo(isolate)), |
1025 | stack_size_(i::FLAG_stack_size), |
1026 | worker_thread_runtime_call_stats_( |
1027 | isolate->counters()->worker_thread_runtime_call_stats()), |
1028 | allocator_(isolate->allocator()), |
1029 | timer_(isolate->counters()->compile_script_on_background()) { |
1030 | VMState<PARSER> state(isolate); |
1031 | |
1032 | // Prepare the data for the internalization phase and compilation phase, which |
1033 | // will happen in the main thread after parsing. |
1034 | LOG(isolate, ScriptEvent(Logger::ScriptEventType::kStreamingCompile, |
1035 | info_->script_id())); |
1036 | info_->set_toplevel(); |
1037 | info_->set_allow_lazy_parsing(); |
1038 | if (V8_UNLIKELY(info_->block_coverage_enabled())) { |
1039 | info_->AllocateSourceRangeMap(); |
1040 | } |
1041 | LanguageMode language_mode = construct_language_mode(FLAG_use_strict); |
1042 | info_->set_language_mode( |
1043 | stricter_language_mode(info_->language_mode(), language_mode)); |
1044 | |
1045 | std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For( |
1046 | streamed_data->source_stream.get(), streamed_data->encoding)); |
1047 | info_->set_character_stream(std::move(stream)); |
1048 | } |
1049 | |
1050 | BackgroundCompileTask::BackgroundCompileTask( |
1051 | AccountingAllocator* allocator, const ParseInfo* outer_parse_info, |
1052 | const AstRawString* function_name, const FunctionLiteral* function_literal, |
1053 | WorkerThreadRuntimeCallStats* worker_thread_runtime_stats, |
1054 | TimedHistogram* timer, int max_stack_size) |
1055 | : info_(ParseInfo::FromParent(outer_parse_info, allocator, function_literal, |
1056 | function_name)), |
1057 | stack_size_(max_stack_size), |
1058 | worker_thread_runtime_call_stats_(worker_thread_runtime_stats), |
1059 | allocator_(allocator), |
1060 | timer_(timer) { |
1061 | DCHECK(outer_parse_info->is_toplevel()); |
1062 | DCHECK(!function_literal->is_toplevel()); |
1063 | |
1064 | // Clone the character stream so both can be accessed independently. |
1065 | std::unique_ptr<Utf16CharacterStream> character_stream = |
1066 | outer_parse_info->character_stream()->Clone(); |
1067 | character_stream->Seek(function_literal->start_position()); |
1068 | info_->set_character_stream(std::move(character_stream)); |
1069 | |
1070 | // Get preparsed scope data from the function literal. |
1071 | if (function_literal->produced_preparse_data()) { |
1072 | ZonePreparseData* serialized_data = |
1073 | function_literal->produced_preparse_data()->Serialize(info_->zone()); |
1074 | info_->set_consumed_preparse_data( |
1075 | ConsumedPreparseData::For(info_->zone(), serialized_data)); |
1076 | } |
1077 | } |
1078 | |
1079 | BackgroundCompileTask::~BackgroundCompileTask() = default; |
1080 | |
1081 | namespace { |
1082 | |
1083 | // A scope object that ensures a parse info's runtime call stats, stack limit |
1084 | // and on_background_thread fields is set correctly during worker-thread |
1085 | // compile, and restores it after going out of scope. |
1086 | class OffThreadParseInfoScope { |
1087 | public: |
1088 | OffThreadParseInfoScope( |
1089 | ParseInfo* parse_info, |
1090 | WorkerThreadRuntimeCallStats* worker_thread_runtime_stats, int stack_size) |
1091 | : parse_info_(parse_info), |
1092 | original_runtime_call_stats_(parse_info_->runtime_call_stats()), |
1093 | original_stack_limit_(parse_info_->stack_limit()), |
1094 | worker_thread_scope_(worker_thread_runtime_stats) { |
1095 | parse_info_->set_on_background_thread(true); |
1096 | parse_info_->set_runtime_call_stats(worker_thread_scope_.Get()); |
1097 | parse_info_->set_stack_limit(GetCurrentStackPosition() - stack_size * KB); |
1098 | } |
1099 | |
1100 | ~OffThreadParseInfoScope() { |
1101 | parse_info_->set_stack_limit(original_stack_limit_); |
1102 | parse_info_->set_runtime_call_stats(original_runtime_call_stats_); |
1103 | parse_info_->set_on_background_thread(false); |
1104 | } |
1105 | |
1106 | private: |
1107 | ParseInfo* parse_info_; |
1108 | RuntimeCallStats* original_runtime_call_stats_; |
1109 | uintptr_t original_stack_limit_; |
1110 | WorkerThreadRuntimeCallStatsScope worker_thread_scope_; |
1111 | |
1112 | DISALLOW_COPY_AND_ASSIGN(OffThreadParseInfoScope); |
1113 | }; |
1114 | |
1115 | } // namespace |
1116 | |
1117 | void BackgroundCompileTask::Run() { |
1118 | DisallowHeapAllocation no_allocation; |
1119 | DisallowHandleAllocation no_handles; |
1120 | DisallowHeapAccess no_heap_access; |
1121 | |
1122 | TimedHistogramScope timer(timer_); |
1123 | OffThreadParseInfoScope off_thread_scope( |
1124 | info_.get(), worker_thread_runtime_call_stats_, stack_size_); |
1125 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
1126 | "BackgroundCompileTask::Run" ); |
1127 | RuntimeCallTimerScope runtimeTimer( |
1128 | info_->runtime_call_stats(), |
1129 | RuntimeCallCounterId::kCompileBackgroundCompileTask); |
1130 | |
1131 | // Update the character stream's runtime call stats. |
1132 | info_->character_stream()->set_runtime_call_stats( |
1133 | info_->runtime_call_stats()); |
1134 | |
1135 | // Parser needs to stay alive for finalizing the parsing on the main |
1136 | // thread. |
1137 | parser_.reset(new Parser(info_.get())); |
1138 | parser_->InitializeEmptyScopeChain(info_.get()); |
1139 | |
1140 | parser_->ParseOnBackground(info_.get()); |
1141 | if (info_->literal() != nullptr) { |
1142 | // Parsing has succeeded, compile. |
1143 | outer_function_job_ = CompileOnBackgroundThread(info_.get(), allocator_, |
1144 | &inner_function_jobs_); |
1145 | } |
1146 | } |
1147 | |
1148 | |
1149 | // ---------------------------------------------------------------------------- |
1150 | // Implementation of Compiler |
1151 | |
1152 | bool Compiler::Analyze(ParseInfo* parse_info) { |
1153 | DCHECK_NOT_NULL(parse_info->literal()); |
1154 | RuntimeCallTimerScope runtimeTimer( |
1155 | parse_info->runtime_call_stats(), |
1156 | parse_info->on_background_thread() |
1157 | ? RuntimeCallCounterId::kCompileBackgroundAnalyse |
1158 | : RuntimeCallCounterId::kCompileAnalyse); |
1159 | if (!Rewriter::Rewrite(parse_info)) return false; |
1160 | if (!DeclarationScope::Analyze(parse_info)) return false; |
1161 | return true; |
1162 | } |
1163 | |
1164 | bool Compiler::ParseAndAnalyze(ParseInfo* parse_info, |
1165 | Handle<SharedFunctionInfo> shared_info, |
1166 | Isolate* isolate) { |
1167 | if (!parsing::ParseAny(parse_info, shared_info, isolate)) { |
1168 | return false; |
1169 | } |
1170 | return Compiler::Analyze(parse_info); |
1171 | } |
1172 | |
1173 | // static |
1174 | bool Compiler::CollectSourcePositions(Isolate* isolate, |
1175 | Handle<SharedFunctionInfo> shared_info) { |
1176 | DCHECK(shared_info->is_compiled()); |
1177 | DCHECK(shared_info->HasBytecodeArray()); |
1178 | DCHECK(!shared_info->GetBytecodeArray()->HasSourcePositionTable()); |
1179 | |
1180 | // Collecting source positions requires allocating a new source position |
1181 | // table. |
1182 | DCHECK(AllowHeapAllocation::IsAllowed()); |
1183 | |
1184 | Handle<BytecodeArray> bytecode = |
1185 | handle(shared_info->GetBytecodeArray(), isolate); |
1186 | |
1187 | // TODO(v8:8510): Push the CLEAR_EXCEPTION flag or something like it down into |
1188 | // the parser so it aborts without setting a pending exception, which then |
1189 | // gets thrown. This would avoid the situation where potentially we'd reparse |
1190 | // several times (running out of stack each time) before hitting this limit. |
1191 | if (GetCurrentStackPosition() < isolate->stack_guard()->real_climit()) { |
1192 | // Stack is already exhausted. |
1193 | bytecode->SetSourcePositionsFailedToCollect(); |
1194 | return false; |
1195 | } |
1196 | |
1197 | DCHECK(AllowCompilation::IsAllowed(isolate)); |
1198 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
1199 | DCHECK(!isolate->has_pending_exception()); |
1200 | VMState<BYTECODE_COMPILER> state(isolate); |
1201 | PostponeInterruptsScope postpone(isolate); |
1202 | RuntimeCallTimerScope runtimeTimer( |
1203 | isolate, RuntimeCallCounterId::kCompileCollectSourcePositions); |
1204 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
1205 | "V8.CollectSourcePositions" ); |
1206 | HistogramTimerScope timer(isolate->counters()->collect_source_positions()); |
1207 | |
1208 | // Set up parse info. |
1209 | ParseInfo parse_info(isolate, shared_info); |
1210 | parse_info.set_lazy_compile(); |
1211 | parse_info.set_collect_source_positions(); |
1212 | if (FLAG_allow_natives_syntax) parse_info.set_allow_natives_syntax(); |
1213 | |
1214 | // Parse and update ParseInfo with the results. |
1215 | if (!parsing::ParseAny(&parse_info, shared_info, isolate)) { |
1216 | // Parsing failed probably as a result of stack exhaustion. |
1217 | bytecode->SetSourcePositionsFailedToCollect(); |
1218 | return FailWithPendingException( |
1219 | isolate, &parse_info, Compiler::ClearExceptionFlag::CLEAR_EXCEPTION); |
1220 | } |
1221 | |
1222 | // Generate the unoptimized bytecode. |
1223 | // TODO(v8:8510): Consider forcing preparsing of inner functions to avoid |
1224 | // wasting time fully parsing them when they won't ever be used. |
1225 | UnoptimizedCompilationJobList inner_function_jobs; |
1226 | std::unique_ptr<UnoptimizedCompilationJob> outer_function_job( |
1227 | GenerateUnoptimizedCode(&parse_info, isolate->allocator(), |
1228 | &inner_function_jobs)); |
1229 | if (!outer_function_job) { |
1230 | // Recompiling failed probably as a result of stack exhaustion. |
1231 | bytecode->SetSourcePositionsFailedToCollect(); |
1232 | return FailWithPendingException( |
1233 | isolate, &parse_info, Compiler::ClearExceptionFlag::CLEAR_EXCEPTION); |
1234 | } |
1235 | |
1236 | DCHECK(outer_function_job->compilation_info()->collect_source_positions()); |
1237 | |
1238 | // TODO(v8:8510) Avoid re-allocating bytecode array/constant pool and |
1239 | // re-internalizeing the ast values. Maybe we could use the |
1240 | // unoptimized_compilation_flag to signal that all we need is the source |
1241 | // position table (and we could do the DCHECK that the bytecode array is the |
1242 | // same in the bytecode-generator, by comparing the real bytecode array on the |
1243 | // SFI with the off-heap bytecode array). |
1244 | |
1245 | // Internalize ast values onto the heap. |
1246 | parse_info.ast_value_factory()->Internalize(isolate); |
1247 | |
1248 | { |
1249 | // Allocate scope infos for the literal. |
1250 | DeclarationScope::AllocateScopeInfos(&parse_info, isolate); |
1251 | CHECK_EQ(outer_function_job->FinalizeJob(shared_info, isolate), |
1252 | CompilationJob::SUCCEEDED); |
1253 | } |
1254 | |
1255 | // Update the source position table on the original bytecode. |
1256 | DCHECK(bytecode->IsBytecodeEqual( |
1257 | *outer_function_job->compilation_info()->bytecode_array())); |
1258 | DCHECK(outer_function_job->compilation_info()->has_bytecode_array()); |
1259 | ByteArray source_position_table = outer_function_job->compilation_info() |
1260 | ->bytecode_array() |
1261 | ->SourcePositionTable(); |
1262 | bytecode->set_source_position_table(source_position_table); |
1263 | // If debugging, make sure that instrumented bytecode has the source position |
1264 | // table set on it as well. |
1265 | if (shared_info->HasDebugInfo() && |
1266 | shared_info->GetDebugInfo()->HasInstrumentedBytecodeArray()) { |
1267 | shared_info->GetDebugBytecodeArray()->set_source_position_table( |
1268 | source_position_table); |
1269 | } |
1270 | |
1271 | DCHECK(!isolate->has_pending_exception()); |
1272 | DCHECK(shared_info->is_compiled_scope().is_compiled()); |
1273 | return true; |
1274 | } |
1275 | |
1276 | bool Compiler::Compile(Handle<SharedFunctionInfo> shared_info, |
1277 | ClearExceptionFlag flag, |
1278 | IsCompiledScope* is_compiled_scope) { |
1279 | // We should never reach here if the function is already compiled. |
1280 | DCHECK(!shared_info->is_compiled()); |
1281 | DCHECK(!is_compiled_scope->is_compiled()); |
1282 | |
1283 | Isolate* isolate = shared_info->GetIsolate(); |
1284 | DCHECK(AllowCompilation::IsAllowed(isolate)); |
1285 | DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); |
1286 | DCHECK(!isolate->has_pending_exception()); |
1287 | DCHECK(!shared_info->HasBytecodeArray()); |
1288 | VMState<BYTECODE_COMPILER> state(isolate); |
1289 | PostponeInterruptsScope postpone(isolate); |
1290 | TimerEventScope<TimerEventCompileCode> compile_timer(isolate); |
1291 | RuntimeCallTimerScope runtimeTimer(isolate, |
1292 | RuntimeCallCounterId::kCompileFunction); |
1293 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), "V8.CompileCode" ); |
1294 | AggregatedHistogramTimerScope timer(isolate->counters()->compile_lazy()); |
1295 | |
1296 | // Set up parse info. |
1297 | ParseInfo parse_info(isolate, shared_info); |
1298 | parse_info.set_lazy_compile(); |
1299 | |
1300 | // Check if the compiler dispatcher has shared_info enqueued for compile. |
1301 | CompilerDispatcher* dispatcher = isolate->compiler_dispatcher(); |
1302 | if (dispatcher->IsEnqueued(shared_info)) { |
1303 | if (!dispatcher->FinishNow(shared_info)) { |
1304 | return FailWithPendingException(isolate, &parse_info, flag); |
1305 | } |
1306 | *is_compiled_scope = shared_info->is_compiled_scope(); |
1307 | DCHECK(is_compiled_scope->is_compiled()); |
1308 | return true; |
1309 | } |
1310 | |
1311 | if (shared_info->HasUncompiledDataWithPreparseData()) { |
1312 | parse_info.set_consumed_preparse_data(ConsumedPreparseData::For( |
1313 | isolate, |
1314 | handle( |
1315 | shared_info->uncompiled_data_with_preparse_data()->preparse_data(), |
1316 | isolate))); |
1317 | } |
1318 | |
1319 | // Parse and update ParseInfo with the results. |
1320 | if (!parsing::ParseAny(&parse_info, shared_info, isolate)) { |
1321 | return FailWithPendingException(isolate, &parse_info, flag); |
1322 | } |
1323 | |
1324 | // Generate the unoptimized bytecode or asm-js data. |
1325 | UnoptimizedCompilationJobList inner_function_jobs; |
1326 | std::unique_ptr<UnoptimizedCompilationJob> outer_function_job( |
1327 | GenerateUnoptimizedCode(&parse_info, isolate->allocator(), |
1328 | &inner_function_jobs)); |
1329 | if (!outer_function_job) { |
1330 | return FailWithPendingException(isolate, &parse_info, flag); |
1331 | } |
1332 | |
1333 | // Internalize ast values onto the heap. |
1334 | parse_info.ast_value_factory()->Internalize(isolate); |
1335 | |
1336 | // Finalize compilation of the unoptimized bytecode or asm-js data. |
1337 | if (!FinalizeUnoptimizedCode(&parse_info, isolate, shared_info, |
1338 | outer_function_job.get(), |
1339 | &inner_function_jobs)) { |
1340 | return FailWithPendingException(isolate, &parse_info, flag); |
1341 | } |
1342 | |
1343 | DCHECK(!isolate->has_pending_exception()); |
1344 | *is_compiled_scope = shared_info->is_compiled_scope(); |
1345 | DCHECK(is_compiled_scope->is_compiled()); |
1346 | return true; |
1347 | } |
1348 | |
1349 | bool Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag flag, |
1350 | IsCompiledScope* is_compiled_scope) { |
1351 | // We should never reach here if the function is already compiled or optimized |
1352 | DCHECK(!function->is_compiled()); |
1353 | DCHECK(!function->IsOptimized()); |
1354 | DCHECK(!function->HasOptimizationMarker()); |
1355 | DCHECK(!function->HasOptimizedCode()); |
1356 | |
1357 | // Reset the JSFunction if we are recompiling due to the bytecode having been |
1358 | // flushed. |
1359 | function->ResetIfBytecodeFlushed(); |
1360 | |
1361 | Isolate* isolate = function->GetIsolate(); |
1362 | Handle<SharedFunctionInfo> shared_info = handle(function->shared(), isolate); |
1363 | |
1364 | // Ensure shared function info is compiled. |
1365 | *is_compiled_scope = shared_info->is_compiled_scope(); |
1366 | if (!is_compiled_scope->is_compiled() && |
1367 | !Compile(shared_info, flag, is_compiled_scope)) { |
1368 | return false; |
1369 | } |
1370 | DCHECK(is_compiled_scope->is_compiled()); |
1371 | Handle<Code> code = handle(shared_info->GetCode(), isolate); |
1372 | |
1373 | // Initialize the feedback cell for this JSFunction. |
1374 | JSFunction::InitializeFeedbackCell(function); |
1375 | |
1376 | // Optimize now if --always-opt is enabled. |
1377 | if (FLAG_always_opt && !function->shared()->HasAsmWasmData()) { |
1378 | if (FLAG_trace_opt) { |
1379 | PrintF("[optimizing " ); |
1380 | function->ShortPrint(); |
1381 | PrintF(" because --always-opt]\n" ); |
1382 | } |
1383 | Handle<Code> opt_code; |
1384 | if (GetOptimizedCode(function, ConcurrencyMode::kNotConcurrent) |
1385 | .ToHandle(&opt_code)) { |
1386 | code = opt_code; |
1387 | } |
1388 | } |
1389 | |
1390 | // Install code on closure. |
1391 | function->set_code(*code); |
1392 | |
1393 | // Check postconditions on success. |
1394 | DCHECK(!isolate->has_pending_exception()); |
1395 | DCHECK(function->shared()->is_compiled()); |
1396 | DCHECK(function->is_compiled()); |
1397 | return true; |
1398 | } |
1399 | |
1400 | bool Compiler::FinalizeBackgroundCompileTask( |
1401 | BackgroundCompileTask* task, Handle<SharedFunctionInfo> shared_info, |
1402 | Isolate* isolate, ClearExceptionFlag flag) { |
1403 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
1404 | "V8.FinalizeBackgroundCompileTask" ); |
1405 | RuntimeCallTimerScope runtimeTimer( |
1406 | isolate, RuntimeCallCounterId::kCompileFinalizeBackgroundCompileTask); |
1407 | HandleScope scope(isolate); |
1408 | ParseInfo* parse_info = task->info(); |
1409 | DCHECK(!parse_info->is_toplevel()); |
1410 | DCHECK(!shared_info->is_compiled()); |
1411 | |
1412 | Handle<Script> script(Script::cast(shared_info->script()), isolate); |
1413 | parse_info->set_script(script); |
1414 | |
1415 | task->parser()->UpdateStatistics(isolate, script); |
1416 | task->parser()->HandleSourceURLComments(isolate, script); |
1417 | |
1418 | if (parse_info->literal() == nullptr || !task->outer_function_job()) { |
1419 | // Parsing or compile failed on background thread - report error messages. |
1420 | return FailWithPendingException(isolate, parse_info, flag); |
1421 | } |
1422 | |
1423 | // Parsing has succeeded - finalize compilation. |
1424 | parse_info->ast_value_factory()->Internalize(isolate); |
1425 | if (!FinalizeUnoptimizedCode(parse_info, isolate, shared_info, |
1426 | task->outer_function_job(), |
1427 | task->inner_function_jobs())) { |
1428 | // Finalization failed - throw an exception. |
1429 | return FailWithPendingException(isolate, parse_info, flag); |
1430 | } |
1431 | |
1432 | DCHECK(!isolate->has_pending_exception()); |
1433 | DCHECK(shared_info->is_compiled()); |
1434 | return true; |
1435 | } |
1436 | |
1437 | bool Compiler::CompileOptimized(Handle<JSFunction> function, |
1438 | ConcurrencyMode mode) { |
1439 | if (function->IsOptimized()) return true; |
1440 | Isolate* isolate = function->GetIsolate(); |
1441 | DCHECK(AllowCompilation::IsAllowed(isolate)); |
1442 | |
1443 | // Start a compilation. |
1444 | Handle<Code> code; |
1445 | if (!GetOptimizedCode(function, mode).ToHandle(&code)) { |
1446 | // Optimization failed, get unoptimized code. Unoptimized code must exist |
1447 | // already if we are optimizing. |
1448 | DCHECK(!isolate->has_pending_exception()); |
1449 | DCHECK(function->shared()->is_compiled()); |
1450 | DCHECK(function->shared()->IsInterpreted()); |
1451 | code = BUILTIN_CODE(isolate, InterpreterEntryTrampoline); |
1452 | } |
1453 | |
1454 | // Install code on closure. |
1455 | function->set_code(*code); |
1456 | |
1457 | // Check postconditions on success. |
1458 | DCHECK(!isolate->has_pending_exception()); |
1459 | DCHECK(function->shared()->is_compiled()); |
1460 | DCHECK(function->is_compiled()); |
1461 | DCHECK_IMPLIES(function->HasOptimizationMarker(), |
1462 | function->IsInOptimizationQueue()); |
1463 | DCHECK_IMPLIES(function->HasOptimizationMarker(), |
1464 | function->ChecksOptimizationMarker()); |
1465 | DCHECK_IMPLIES(function->IsInOptimizationQueue(), |
1466 | mode == ConcurrencyMode::kConcurrent); |
1467 | return true; |
1468 | } |
1469 | |
1470 | MaybeHandle<SharedFunctionInfo> Compiler::CompileForLiveEdit( |
1471 | ParseInfo* parse_info, Isolate* isolate) { |
1472 | IsCompiledScope is_compiled_scope; |
1473 | return CompileToplevel(parse_info, isolate, &is_compiled_scope); |
1474 | } |
1475 | |
1476 | MaybeHandle<JSFunction> Compiler::GetFunctionFromEval( |
1477 | Handle<String> source, Handle<SharedFunctionInfo> outer_info, |
1478 | Handle<Context> context, LanguageMode language_mode, |
1479 | ParseRestriction restriction, int parameters_end_pos, |
1480 | int eval_scope_position, int eval_position) { |
1481 | Isolate* isolate = context->GetIsolate(); |
1482 | int source_length = source->length(); |
1483 | isolate->counters()->total_eval_size()->Increment(source_length); |
1484 | isolate->counters()->total_compile_size()->Increment(source_length); |
1485 | |
1486 | // The cache lookup key needs to be aware of the separation between the |
1487 | // parameters and the body to prevent this valid invocation: |
1488 | // Function("", "function anonymous(\n/**/) {\n}"); |
1489 | // from adding an entry that falsely approves this invalid invocation: |
1490 | // Function("\n/**/) {\nfunction anonymous(", "}"); |
1491 | // The actual eval_scope_position for indirect eval and CreateDynamicFunction |
1492 | // is unused (just 0), which means it's an available field to use to indicate |
1493 | // this separation. But to make sure we're not causing other false hits, we |
1494 | // negate the scope position. |
1495 | if (restriction == ONLY_SINGLE_FUNCTION_LITERAL && |
1496 | parameters_end_pos != kNoSourcePosition) { |
1497 | // use the parameters_end_pos as the eval_scope_position in the eval cache. |
1498 | DCHECK_EQ(eval_scope_position, 0); |
1499 | eval_scope_position = -parameters_end_pos; |
1500 | } |
1501 | CompilationCache* compilation_cache = isolate->compilation_cache(); |
1502 | InfoCellPair eval_result = compilation_cache->LookupEval( |
1503 | source, outer_info, context, language_mode, eval_scope_position); |
1504 | Handle<FeedbackCell> feedback_cell; |
1505 | if (eval_result.has_feedback_cell()) { |
1506 | feedback_cell = handle(eval_result.feedback_cell(), isolate); |
1507 | } |
1508 | |
1509 | Handle<SharedFunctionInfo> shared_info; |
1510 | Handle<Script> script; |
1511 | IsCompiledScope is_compiled_scope; |
1512 | bool allow_eval_cache; |
1513 | if (eval_result.has_shared()) { |
1514 | shared_info = Handle<SharedFunctionInfo>(eval_result.shared(), isolate); |
1515 | script = Handle<Script>(Script::cast(shared_info->script()), isolate); |
1516 | is_compiled_scope = shared_info->is_compiled_scope(); |
1517 | allow_eval_cache = true; |
1518 | } else { |
1519 | ParseInfo parse_info(isolate); |
1520 | script = parse_info.CreateScript( |
1521 | isolate, source, OriginOptionsForEval(outer_info->script())); |
1522 | script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); |
1523 | script->set_eval_from_shared(*outer_info); |
1524 | if (eval_position == kNoSourcePosition) { |
1525 | // If the position is missing, attempt to get the code offset by |
1526 | // walking the stack. Do not translate the code offset into source |
1527 | // position, but store it as negative value for lazy translation. |
1528 | StackTraceFrameIterator it(isolate); |
1529 | if (!it.done() && it.is_javascript()) { |
1530 | FrameSummary summary = FrameSummary::GetTop(it.javascript_frame()); |
1531 | script->set_eval_from_shared( |
1532 | summary.AsJavaScript().function()->shared()); |
1533 | script->set_origin_options(OriginOptionsForEval(*summary.script())); |
1534 | eval_position = -summary.code_offset(); |
1535 | } else { |
1536 | eval_position = 0; |
1537 | } |
1538 | } |
1539 | script->set_eval_from_position(eval_position); |
1540 | TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
1541 | TRACE_DISABLED_BY_DEFAULT("v8.compile" ), "Script" , |
1542 | TRACE_ID_WITH_SCOPE(Script::kTraceScope, script->id()), |
1543 | script->ToTracedValue()); |
1544 | |
1545 | parse_info.set_eval(); |
1546 | parse_info.set_language_mode(language_mode); |
1547 | parse_info.set_parse_restriction(restriction); |
1548 | parse_info.set_parameters_end_pos(parameters_end_pos); |
1549 | if (!context->IsNativeContext()) { |
1550 | parse_info.set_outer_scope_info(handle(context->scope_info(), isolate)); |
1551 | } |
1552 | DCHECK(!parse_info.is_module()); |
1553 | |
1554 | if (!CompileToplevel(&parse_info, isolate, &is_compiled_scope) |
1555 | .ToHandle(&shared_info)) { |
1556 | return MaybeHandle<JSFunction>(); |
1557 | } |
1558 | allow_eval_cache = parse_info.allow_eval_cache(); |
1559 | } |
1560 | |
1561 | // If caller is strict mode, the result must be in strict mode as well. |
1562 | DCHECK(is_sloppy(language_mode) || is_strict(shared_info->language_mode())); |
1563 | |
1564 | Handle<JSFunction> result; |
1565 | if (eval_result.has_shared()) { |
1566 | if (eval_result.has_feedback_cell()) { |
1567 | result = isolate->factory()->NewFunctionFromSharedFunctionInfo( |
1568 | shared_info, context, feedback_cell, AllocationType::kYoung); |
1569 | } else { |
1570 | result = isolate->factory()->NewFunctionFromSharedFunctionInfo( |
1571 | shared_info, context, AllocationType::kYoung); |
1572 | JSFunction::InitializeFeedbackCell(result); |
1573 | if (allow_eval_cache) { |
1574 | // Make sure to cache this result. |
1575 | Handle<FeedbackCell> new_feedback_cell(result->raw_feedback_cell(), |
1576 | isolate); |
1577 | compilation_cache->PutEval(source, outer_info, context, shared_info, |
1578 | new_feedback_cell, eval_scope_position); |
1579 | } |
1580 | } |
1581 | } else { |
1582 | result = isolate->factory()->NewFunctionFromSharedFunctionInfo( |
1583 | shared_info, context, AllocationType::kYoung); |
1584 | JSFunction::InitializeFeedbackCell(result); |
1585 | if (allow_eval_cache) { |
1586 | // Add the SharedFunctionInfo and the LiteralsArray to the eval cache if |
1587 | // we didn't retrieve from there. |
1588 | Handle<FeedbackCell> new_feedback_cell(result->raw_feedback_cell(), |
1589 | isolate); |
1590 | compilation_cache->PutEval(source, outer_info, context, shared_info, |
1591 | new_feedback_cell, eval_scope_position); |
1592 | } |
1593 | } |
1594 | DCHECK(is_compiled_scope.is_compiled()); |
1595 | |
1596 | return result; |
1597 | } |
1598 | |
1599 | |
1600 | bool Compiler::CodeGenerationFromStringsAllowed(Isolate* isolate, |
1601 | Handle<Context> context, |
1602 | Handle<String> source) { |
1603 | DCHECK(context->allow_code_gen_from_strings()->IsFalse(isolate)); |
1604 | // Check with callback if set. |
1605 | AllowCodeGenerationFromStringsCallback callback = |
1606 | isolate->allow_code_gen_callback(); |
1607 | if (callback == nullptr) { |
1608 | // No callback set and code generation disallowed. |
1609 | return false; |
1610 | } else { |
1611 | // Callback set. Let it decide if code generation is allowed. |
1612 | VMState<EXTERNAL> state(isolate); |
1613 | return callback(v8::Utils::ToLocal(context), v8::Utils::ToLocal(source)); |
1614 | } |
1615 | } |
1616 | |
1617 | MaybeHandle<JSFunction> Compiler::GetFunctionFromString( |
1618 | Handle<Context> context, Handle<String> source, |
1619 | ParseRestriction restriction, int parameters_end_pos) { |
1620 | Isolate* const isolate = context->GetIsolate(); |
1621 | Handle<Context> native_context(context->native_context(), isolate); |
1622 | |
1623 | // Check if native context allows code generation from |
1624 | // strings. Throw an exception if it doesn't. |
1625 | if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) && |
1626 | !CodeGenerationFromStringsAllowed(isolate, native_context, source)) { |
1627 | Handle<Object> error_message = |
1628 | native_context->ErrorMessageForCodeGenerationFromStrings(); |
1629 | THROW_NEW_ERROR(isolate, NewEvalError(MessageTemplate::kCodeGenFromStrings, |
1630 | error_message), |
1631 | JSFunction); |
1632 | } |
1633 | |
1634 | // Compile source string in the native context. |
1635 | int eval_scope_position = 0; |
1636 | int eval_position = kNoSourcePosition; |
1637 | Handle<SharedFunctionInfo> outer_info( |
1638 | native_context->empty_function()->shared(), isolate); |
1639 | return Compiler::GetFunctionFromEval( |
1640 | source, outer_info, native_context, LanguageMode::kSloppy, restriction, |
1641 | parameters_end_pos, eval_scope_position, eval_position); |
1642 | } |
1643 | |
1644 | namespace { |
1645 | |
1646 | struct ScriptCompileTimerScope { |
1647 | public: |
1648 | // TODO(leszeks): There are too many blink-specific entries in this enum, |
1649 | // figure out a way to push produce/hit-isolate-cache/consume/consume-failed |
1650 | // back up the API and log them in blink instead. |
1651 | enum class CacheBehaviour { |
1652 | kProduceCodeCache, |
1653 | kHitIsolateCacheWhenNoCache, |
1654 | kConsumeCodeCache, |
1655 | kConsumeCodeCacheFailed, |
1656 | kNoCacheBecauseInlineScript, |
1657 | kNoCacheBecauseScriptTooSmall, |
1658 | kNoCacheBecauseCacheTooCold, |
1659 | kNoCacheNoReason, |
1660 | kNoCacheBecauseNoResource, |
1661 | kNoCacheBecauseInspector, |
1662 | kNoCacheBecauseCachingDisabled, |
1663 | kNoCacheBecauseModule, |
1664 | kNoCacheBecauseStreamingSource, |
1665 | kNoCacheBecauseV8Extension, |
1666 | kHitIsolateCacheWhenProduceCodeCache, |
1667 | kHitIsolateCacheWhenConsumeCodeCache, |
1668 | kNoCacheBecauseExtensionModule, |
1669 | kNoCacheBecausePacScript, |
1670 | kNoCacheBecauseInDocumentWrite, |
1671 | kNoCacheBecauseResourceWithNoCacheHandler, |
1672 | kHitIsolateCacheWhenStreamingSource, |
1673 | kCount |
1674 | }; |
1675 | |
1676 | explicit ScriptCompileTimerScope( |
1677 | Isolate* isolate, ScriptCompiler::NoCacheReason no_cache_reason) |
1678 | : isolate_(isolate), |
1679 | all_scripts_histogram_scope_(isolate->counters()->compile_script(), |
1680 | true), |
1681 | no_cache_reason_(no_cache_reason), |
1682 | hit_isolate_cache_(false), |
1683 | producing_code_cache_(false), |
1684 | consuming_code_cache_(false), |
1685 | consuming_code_cache_failed_(false) {} |
1686 | |
1687 | ~ScriptCompileTimerScope() { |
1688 | CacheBehaviour cache_behaviour = GetCacheBehaviour(); |
1689 | |
1690 | Histogram* cache_behaviour_histogram = |
1691 | isolate_->counters()->compile_script_cache_behaviour(); |
1692 | // Sanity check that the histogram has exactly one bin per enum entry. |
1693 | DCHECK_EQ(0, cache_behaviour_histogram->min()); |
1694 | DCHECK_EQ(static_cast<int>(CacheBehaviour::kCount), |
1695 | cache_behaviour_histogram->max() + 1); |
1696 | DCHECK_EQ(static_cast<int>(CacheBehaviour::kCount), |
1697 | cache_behaviour_histogram->num_buckets()); |
1698 | cache_behaviour_histogram->AddSample(static_cast<int>(cache_behaviour)); |
1699 | |
1700 | histogram_scope_.set_histogram( |
1701 | GetCacheBehaviourTimedHistogram(cache_behaviour)); |
1702 | } |
1703 | |
1704 | void set_hit_isolate_cache() { hit_isolate_cache_ = true; } |
1705 | |
1706 | void set_producing_code_cache() { producing_code_cache_ = true; } |
1707 | |
1708 | void set_consuming_code_cache() { consuming_code_cache_ = true; } |
1709 | |
1710 | void set_consuming_code_cache_failed() { |
1711 | consuming_code_cache_failed_ = true; |
1712 | } |
1713 | |
1714 | private: |
1715 | Isolate* isolate_; |
1716 | LazyTimedHistogramScope histogram_scope_; |
1717 | // TODO(leszeks): This timer is the sum of the other times, consider removing |
1718 | // it to save space. |
1719 | HistogramTimerScope all_scripts_histogram_scope_; |
1720 | ScriptCompiler::NoCacheReason no_cache_reason_; |
1721 | bool hit_isolate_cache_; |
1722 | bool producing_code_cache_; |
1723 | bool consuming_code_cache_; |
1724 | bool consuming_code_cache_failed_; |
1725 | |
1726 | CacheBehaviour GetCacheBehaviour() { |
1727 | if (producing_code_cache_) { |
1728 | if (hit_isolate_cache_) { |
1729 | return CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache; |
1730 | } else { |
1731 | return CacheBehaviour::kProduceCodeCache; |
1732 | } |
1733 | } |
1734 | |
1735 | if (consuming_code_cache_) { |
1736 | if (hit_isolate_cache_) { |
1737 | return CacheBehaviour::kHitIsolateCacheWhenConsumeCodeCache; |
1738 | } else if (consuming_code_cache_failed_) { |
1739 | return CacheBehaviour::kConsumeCodeCacheFailed; |
1740 | } |
1741 | return CacheBehaviour::kConsumeCodeCache; |
1742 | } |
1743 | |
1744 | if (hit_isolate_cache_) { |
1745 | if (no_cache_reason_ == ScriptCompiler::kNoCacheBecauseStreamingSource) { |
1746 | return CacheBehaviour::kHitIsolateCacheWhenStreamingSource; |
1747 | } |
1748 | return CacheBehaviour::kHitIsolateCacheWhenNoCache; |
1749 | } |
1750 | |
1751 | switch (no_cache_reason_) { |
1752 | case ScriptCompiler::kNoCacheBecauseInlineScript: |
1753 | return CacheBehaviour::kNoCacheBecauseInlineScript; |
1754 | case ScriptCompiler::kNoCacheBecauseScriptTooSmall: |
1755 | return CacheBehaviour::kNoCacheBecauseScriptTooSmall; |
1756 | case ScriptCompiler::kNoCacheBecauseCacheTooCold: |
1757 | return CacheBehaviour::kNoCacheBecauseCacheTooCold; |
1758 | case ScriptCompiler::kNoCacheNoReason: |
1759 | return CacheBehaviour::kNoCacheNoReason; |
1760 | case ScriptCompiler::kNoCacheBecauseNoResource: |
1761 | return CacheBehaviour::kNoCacheBecauseNoResource; |
1762 | case ScriptCompiler::kNoCacheBecauseInspector: |
1763 | return CacheBehaviour::kNoCacheBecauseInspector; |
1764 | case ScriptCompiler::kNoCacheBecauseCachingDisabled: |
1765 | return CacheBehaviour::kNoCacheBecauseCachingDisabled; |
1766 | case ScriptCompiler::kNoCacheBecauseModule: |
1767 | return CacheBehaviour::kNoCacheBecauseModule; |
1768 | case ScriptCompiler::kNoCacheBecauseStreamingSource: |
1769 | return CacheBehaviour::kNoCacheBecauseStreamingSource; |
1770 | case ScriptCompiler::kNoCacheBecauseV8Extension: |
1771 | return CacheBehaviour::kNoCacheBecauseV8Extension; |
1772 | case ScriptCompiler::kNoCacheBecauseExtensionModule: |
1773 | return CacheBehaviour::kNoCacheBecauseExtensionModule; |
1774 | case ScriptCompiler::kNoCacheBecausePacScript: |
1775 | return CacheBehaviour::kNoCacheBecausePacScript; |
1776 | case ScriptCompiler::kNoCacheBecauseInDocumentWrite: |
1777 | return CacheBehaviour::kNoCacheBecauseInDocumentWrite; |
1778 | case ScriptCompiler::kNoCacheBecauseResourceWithNoCacheHandler: |
1779 | return CacheBehaviour::kNoCacheBecauseResourceWithNoCacheHandler; |
1780 | case ScriptCompiler::kNoCacheBecauseDeferredProduceCodeCache: { |
1781 | if (hit_isolate_cache_) { |
1782 | return CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache; |
1783 | } else { |
1784 | return CacheBehaviour::kProduceCodeCache; |
1785 | } |
1786 | } |
1787 | } |
1788 | UNREACHABLE(); |
1789 | } |
1790 | |
1791 | TimedHistogram* GetCacheBehaviourTimedHistogram( |
1792 | CacheBehaviour cache_behaviour) { |
1793 | switch (cache_behaviour) { |
1794 | case CacheBehaviour::kProduceCodeCache: |
1795 | // Even if we hit the isolate's compilation cache, we currently recompile |
1796 | // when we want to produce the code cache. |
1797 | case CacheBehaviour::kHitIsolateCacheWhenProduceCodeCache: |
1798 | return isolate_->counters()->compile_script_with_produce_cache(); |
1799 | case CacheBehaviour::kHitIsolateCacheWhenNoCache: |
1800 | case CacheBehaviour::kHitIsolateCacheWhenConsumeCodeCache: |
1801 | case CacheBehaviour::kHitIsolateCacheWhenStreamingSource: |
1802 | return isolate_->counters()->compile_script_with_isolate_cache_hit(); |
1803 | case CacheBehaviour::kConsumeCodeCacheFailed: |
1804 | return isolate_->counters()->compile_script_consume_failed(); |
1805 | case CacheBehaviour::kConsumeCodeCache: |
1806 | return isolate_->counters()->compile_script_with_consume_cache(); |
1807 | |
1808 | case CacheBehaviour::kNoCacheBecauseInlineScript: |
1809 | return isolate_->counters() |
1810 | ->compile_script_no_cache_because_inline_script(); |
1811 | case CacheBehaviour::kNoCacheBecauseScriptTooSmall: |
1812 | return isolate_->counters() |
1813 | ->compile_script_no_cache_because_script_too_small(); |
1814 | case CacheBehaviour::kNoCacheBecauseCacheTooCold: |
1815 | return isolate_->counters() |
1816 | ->compile_script_no_cache_because_cache_too_cold(); |
1817 | |
1818 | // Aggregate all the other "no cache" counters into a single histogram, to |
1819 | // save space. |
1820 | case CacheBehaviour::kNoCacheNoReason: |
1821 | case CacheBehaviour::kNoCacheBecauseNoResource: |
1822 | case CacheBehaviour::kNoCacheBecauseInspector: |
1823 | case CacheBehaviour::kNoCacheBecauseCachingDisabled: |
1824 | // TODO(leszeks): Consider counting separately once modules are more |
1825 | // common. |
1826 | case CacheBehaviour::kNoCacheBecauseModule: |
1827 | // TODO(leszeks): Count separately or remove entirely once we have |
1828 | // background compilation. |
1829 | case CacheBehaviour::kNoCacheBecauseStreamingSource: |
1830 | case CacheBehaviour::kNoCacheBecauseV8Extension: |
1831 | case CacheBehaviour::kNoCacheBecauseExtensionModule: |
1832 | case CacheBehaviour::kNoCacheBecausePacScript: |
1833 | case CacheBehaviour::kNoCacheBecauseInDocumentWrite: |
1834 | case CacheBehaviour::kNoCacheBecauseResourceWithNoCacheHandler: |
1835 | return isolate_->counters()->compile_script_no_cache_other(); |
1836 | |
1837 | case CacheBehaviour::kCount: |
1838 | UNREACHABLE(); |
1839 | } |
1840 | UNREACHABLE(); |
1841 | } |
1842 | }; |
1843 | |
1844 | Handle<Script> NewScript(Isolate* isolate, ParseInfo* parse_info, |
1845 | Handle<String> source, |
1846 | Compiler::ScriptDetails script_details, |
1847 | ScriptOriginOptions origin_options, |
1848 | NativesFlag natives) { |
1849 | // Create a script object describing the script to be compiled. |
1850 | Handle<Script> script = |
1851 | parse_info->CreateScript(isolate, source, origin_options, natives); |
1852 | Handle<Object> script_name; |
1853 | if (script_details.name_obj.ToHandle(&script_name)) { |
1854 | script->set_name(*script_name); |
1855 | script->set_line_offset(script_details.line_offset); |
1856 | script->set_column_offset(script_details.column_offset); |
1857 | } |
1858 | Handle<Object> source_map_url; |
1859 | if (script_details.source_map_url.ToHandle(&source_map_url)) { |
1860 | script->set_source_mapping_url(*source_map_url); |
1861 | } |
1862 | Handle<FixedArray> host_defined_options; |
1863 | if (script_details.host_defined_options.ToHandle(&host_defined_options)) { |
1864 | script->set_host_defined_options(*host_defined_options); |
1865 | } |
1866 | LOG(isolate, ScriptDetails(*script)); |
1867 | TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
1868 | TRACE_DISABLED_BY_DEFAULT("v8.compile" ), "Script" , |
1869 | TRACE_ID_WITH_SCOPE(Script::kTraceScope, script->id()), |
1870 | script->ToTracedValue()); |
1871 | return script; |
1872 | } |
1873 | |
1874 | } // namespace |
1875 | |
1876 | MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript( |
1877 | Isolate* isolate, Handle<String> source, |
1878 | const Compiler::ScriptDetails& script_details, |
1879 | ScriptOriginOptions origin_options, v8::Extension* extension, |
1880 | ScriptData* cached_data, ScriptCompiler::CompileOptions compile_options, |
1881 | ScriptCompiler::NoCacheReason no_cache_reason, NativesFlag natives) { |
1882 | ScriptCompileTimerScope compile_timer(isolate, no_cache_reason); |
1883 | |
1884 | if (compile_options == ScriptCompiler::kNoCompileOptions || |
1885 | compile_options == ScriptCompiler::kEagerCompile) { |
1886 | DCHECK_NULL(cached_data); |
1887 | } else { |
1888 | DCHECK(compile_options == ScriptCompiler::kConsumeCodeCache); |
1889 | DCHECK(cached_data); |
1890 | DCHECK_NULL(extension); |
1891 | } |
1892 | int source_length = source->length(); |
1893 | isolate->counters()->total_load_size()->Increment(source_length); |
1894 | isolate->counters()->total_compile_size()->Increment(source_length); |
1895 | |
1896 | LanguageMode language_mode = construct_language_mode(FLAG_use_strict); |
1897 | CompilationCache* compilation_cache = isolate->compilation_cache(); |
1898 | |
1899 | // Do a lookup in the compilation cache but not for extensions. |
1900 | MaybeHandle<SharedFunctionInfo> maybe_result; |
1901 | IsCompiledScope is_compiled_scope; |
1902 | if (extension == nullptr) { |
1903 | bool can_consume_code_cache = |
1904 | compile_options == ScriptCompiler::kConsumeCodeCache; |
1905 | if (can_consume_code_cache) { |
1906 | compile_timer.set_consuming_code_cache(); |
1907 | } |
1908 | |
1909 | // First check per-isolate compilation cache. |
1910 | maybe_result = compilation_cache->LookupScript( |
1911 | source, script_details.name_obj, script_details.line_offset, |
1912 | script_details.column_offset, origin_options, isolate->native_context(), |
1913 | language_mode); |
1914 | if (!maybe_result.is_null()) { |
1915 | compile_timer.set_hit_isolate_cache(); |
1916 | } else if (can_consume_code_cache) { |
1917 | compile_timer.set_consuming_code_cache(); |
1918 | // Then check cached code provided by embedder. |
1919 | HistogramTimerScope timer(isolate->counters()->compile_deserialize()); |
1920 | RuntimeCallTimerScope runtimeTimer( |
1921 | isolate, RuntimeCallCounterId::kCompileDeserialize); |
1922 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
1923 | "V8.CompileDeserialize" ); |
1924 | Handle<SharedFunctionInfo> inner_result; |
1925 | if (CodeSerializer::Deserialize(isolate, cached_data, source, |
1926 | origin_options) |
1927 | .ToHandle(&inner_result) && |
1928 | inner_result->is_compiled()) { |
1929 | // Promote to per-isolate compilation cache. |
1930 | is_compiled_scope = inner_result->is_compiled_scope(); |
1931 | DCHECK(is_compiled_scope.is_compiled()); |
1932 | compilation_cache->PutScript(source, isolate->native_context(), |
1933 | language_mode, inner_result); |
1934 | Handle<Script> script(Script::cast(inner_result->script()), isolate); |
1935 | maybe_result = inner_result; |
1936 | } else { |
1937 | // Deserializer failed. Fall through to compile. |
1938 | compile_timer.set_consuming_code_cache_failed(); |
1939 | } |
1940 | } |
1941 | } |
1942 | |
1943 | if (maybe_result.is_null()) { |
1944 | ParseInfo parse_info(isolate); |
1945 | // No cache entry found compile the script. |
1946 | NewScript(isolate, &parse_info, source, script_details, origin_options, |
1947 | natives); |
1948 | |
1949 | // Compile the function and add it to the isolate cache. |
1950 | if (origin_options.IsModule()) parse_info.set_module(); |
1951 | parse_info.set_extension(extension); |
1952 | parse_info.set_eager(compile_options == ScriptCompiler::kEagerCompile); |
1953 | |
1954 | parse_info.set_language_mode( |
1955 | stricter_language_mode(parse_info.language_mode(), language_mode)); |
1956 | maybe_result = CompileToplevel(&parse_info, isolate, &is_compiled_scope); |
1957 | Handle<SharedFunctionInfo> result; |
1958 | if (extension == nullptr && maybe_result.ToHandle(&result)) { |
1959 | DCHECK(is_compiled_scope.is_compiled()); |
1960 | compilation_cache->PutScript(source, isolate->native_context(), |
1961 | language_mode, result); |
1962 | } else if (maybe_result.is_null() && natives != EXTENSION_CODE) { |
1963 | isolate->ReportPendingMessages(); |
1964 | } |
1965 | } |
1966 | |
1967 | return maybe_result; |
1968 | } |
1969 | |
1970 | MaybeHandle<JSFunction> Compiler::GetWrappedFunction( |
1971 | Handle<String> source, Handle<FixedArray> arguments, |
1972 | Handle<Context> context, const Compiler::ScriptDetails& script_details, |
1973 | ScriptOriginOptions origin_options, ScriptData* cached_data, |
1974 | v8::ScriptCompiler::CompileOptions compile_options, |
1975 | v8::ScriptCompiler::NoCacheReason no_cache_reason) { |
1976 | Isolate* isolate = context->GetIsolate(); |
1977 | ScriptCompileTimerScope compile_timer(isolate, no_cache_reason); |
1978 | |
1979 | if (compile_options == ScriptCompiler::kNoCompileOptions || |
1980 | compile_options == ScriptCompiler::kEagerCompile) { |
1981 | DCHECK_NULL(cached_data); |
1982 | } else { |
1983 | DCHECK(compile_options == ScriptCompiler::kConsumeCodeCache); |
1984 | DCHECK(cached_data); |
1985 | } |
1986 | |
1987 | int source_length = source->length(); |
1988 | isolate->counters()->total_compile_size()->Increment(source_length); |
1989 | |
1990 | LanguageMode language_mode = construct_language_mode(FLAG_use_strict); |
1991 | |
1992 | MaybeHandle<SharedFunctionInfo> maybe_result; |
1993 | bool can_consume_code_cache = |
1994 | compile_options == ScriptCompiler::kConsumeCodeCache; |
1995 | if (can_consume_code_cache) { |
1996 | compile_timer.set_consuming_code_cache(); |
1997 | // Then check cached code provided by embedder. |
1998 | HistogramTimerScope timer(isolate->counters()->compile_deserialize()); |
1999 | RuntimeCallTimerScope runtimeTimer( |
2000 | isolate, RuntimeCallCounterId::kCompileDeserialize); |
2001 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
2002 | "V8.CompileDeserialize" ); |
2003 | maybe_result = CodeSerializer::Deserialize(isolate, cached_data, source, |
2004 | origin_options); |
2005 | if (maybe_result.is_null()) { |
2006 | // Deserializer failed. Fall through to compile. |
2007 | compile_timer.set_consuming_code_cache_failed(); |
2008 | } |
2009 | } |
2010 | |
2011 | Handle<SharedFunctionInfo> wrapped; |
2012 | Handle<Script> script; |
2013 | IsCompiledScope is_compiled_scope; |
2014 | if (!maybe_result.ToHandle(&wrapped)) { |
2015 | ParseInfo parse_info(isolate); |
2016 | script = NewScript(isolate, &parse_info, source, script_details, |
2017 | origin_options, NOT_NATIVES_CODE); |
2018 | script->set_wrapped_arguments(*arguments); |
2019 | |
2020 | parse_info.set_eval(); // Use an eval scope as declaration scope. |
2021 | parse_info.set_wrapped_as_function(); |
2022 | // parse_info.set_eager(compile_options == ScriptCompiler::kEagerCompile); |
2023 | if (!context->IsNativeContext()) { |
2024 | parse_info.set_outer_scope_info(handle(context->scope_info(), isolate)); |
2025 | } |
2026 | parse_info.set_language_mode( |
2027 | stricter_language_mode(parse_info.language_mode(), language_mode)); |
2028 | |
2029 | Handle<SharedFunctionInfo> top_level; |
2030 | maybe_result = CompileToplevel(&parse_info, isolate, &is_compiled_scope); |
2031 | if (maybe_result.is_null()) isolate->ReportPendingMessages(); |
2032 | ASSIGN_RETURN_ON_EXCEPTION(isolate, top_level, maybe_result, JSFunction); |
2033 | |
2034 | SharedFunctionInfo::ScriptIterator infos(isolate, *script); |
2035 | for (SharedFunctionInfo info = infos.Next(); !info.is_null(); |
2036 | info = infos.Next()) { |
2037 | if (info->is_wrapped()) { |
2038 | wrapped = Handle<SharedFunctionInfo>(info, isolate); |
2039 | break; |
2040 | } |
2041 | } |
2042 | DCHECK(!wrapped.is_null()); |
2043 | } else { |
2044 | is_compiled_scope = wrapped->is_compiled_scope(); |
2045 | script = Handle<Script>(Script::cast(wrapped->script()), isolate); |
2046 | } |
2047 | DCHECK(is_compiled_scope.is_compiled()); |
2048 | |
2049 | return isolate->factory()->NewFunctionFromSharedFunctionInfo( |
2050 | wrapped, context, AllocationType::kYoung); |
2051 | } |
2052 | |
2053 | MaybeHandle<SharedFunctionInfo> |
2054 | Compiler::GetSharedFunctionInfoForStreamedScript( |
2055 | Isolate* isolate, Handle<String> source, |
2056 | const ScriptDetails& script_details, ScriptOriginOptions origin_options, |
2057 | ScriptStreamingData* streaming_data) { |
2058 | ScriptCompileTimerScope compile_timer( |
2059 | isolate, ScriptCompiler::kNoCacheBecauseStreamingSource); |
2060 | PostponeInterruptsScope postpone(isolate); |
2061 | |
2062 | int source_length = source->length(); |
2063 | isolate->counters()->total_load_size()->Increment(source_length); |
2064 | isolate->counters()->total_compile_size()->Increment(source_length); |
2065 | |
2066 | BackgroundCompileTask* task = streaming_data->task.get(); |
2067 | ParseInfo* parse_info = task->info(); |
2068 | DCHECK(parse_info->is_toplevel()); |
2069 | // Check if compile cache already holds the SFI, if so no need to finalize |
2070 | // the code compiled on the background thread. |
2071 | CompilationCache* compilation_cache = isolate->compilation_cache(); |
2072 | MaybeHandle<SharedFunctionInfo> maybe_result = |
2073 | compilation_cache->LookupScript( |
2074 | source, script_details.name_obj, script_details.line_offset, |
2075 | script_details.column_offset, origin_options, |
2076 | isolate->native_context(), parse_info->language_mode()); |
2077 | if (!maybe_result.is_null()) { |
2078 | compile_timer.set_hit_isolate_cache(); |
2079 | } |
2080 | |
2081 | if (maybe_result.is_null()) { |
2082 | // No cache entry found, finalize compilation of the script and add it to |
2083 | // the isolate cache. |
2084 | Handle<Script> script = |
2085 | NewScript(isolate, parse_info, source, script_details, origin_options, |
2086 | NOT_NATIVES_CODE); |
2087 | task->parser()->UpdateStatistics(isolate, script); |
2088 | task->parser()->HandleSourceURLComments(isolate, script); |
2089 | |
2090 | if (parse_info->literal() == nullptr || !task->outer_function_job()) { |
2091 | // Parsing has failed - report error messages. |
2092 | FailWithPendingException(isolate, parse_info, |
2093 | Compiler::ClearExceptionFlag::KEEP_EXCEPTION); |
2094 | } else { |
2095 | // Parsing has succeeded - finalize compilation. |
2096 | maybe_result = |
2097 | FinalizeTopLevel(parse_info, isolate, task->outer_function_job(), |
2098 | task->inner_function_jobs()); |
2099 | if (maybe_result.is_null()) { |
2100 | // Finalization failed - throw an exception. |
2101 | FailWithPendingException(isolate, parse_info, |
2102 | Compiler::ClearExceptionFlag::KEEP_EXCEPTION); |
2103 | } |
2104 | } |
2105 | |
2106 | // Add compiled code to the isolate cache. |
2107 | Handle<SharedFunctionInfo> result; |
2108 | if (maybe_result.ToHandle(&result)) { |
2109 | compilation_cache->PutScript(source, isolate->native_context(), |
2110 | parse_info->language_mode(), result); |
2111 | } |
2112 | } |
2113 | |
2114 | streaming_data->Release(); |
2115 | return maybe_result; |
2116 | } |
2117 | |
2118 | Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo( |
2119 | FunctionLiteral* literal, Handle<Script> script, Isolate* isolate) { |
2120 | // Precondition: code has been parsed and scopes have been analyzed. |
2121 | MaybeHandle<SharedFunctionInfo> maybe_existing; |
2122 | |
2123 | // Find any previously allocated shared function info for the given literal. |
2124 | maybe_existing = script->FindSharedFunctionInfo(isolate, literal); |
2125 | |
2126 | // If we found an existing shared function info, return it. |
2127 | Handle<SharedFunctionInfo> existing; |
2128 | if (maybe_existing.ToHandle(&existing)) return existing; |
2129 | |
2130 | // Allocate a shared function info object which will be compiled lazily. |
2131 | Handle<SharedFunctionInfo> result = |
2132 | isolate->factory()->NewSharedFunctionInfoForLiteral(literal, script, |
2133 | false); |
2134 | return result; |
2135 | } |
2136 | |
2137 | MaybeHandle<Code> Compiler::GetOptimizedCodeForOSR(Handle<JSFunction> function, |
2138 | BailoutId osr_offset, |
2139 | JavaScriptFrame* osr_frame) { |
2140 | DCHECK(!osr_offset.IsNone()); |
2141 | DCHECK_NOT_NULL(osr_frame); |
2142 | return GetOptimizedCode(function, ConcurrencyMode::kNotConcurrent, osr_offset, |
2143 | osr_frame); |
2144 | } |
2145 | |
2146 | bool Compiler::FinalizeOptimizedCompilationJob(OptimizedCompilationJob* job, |
2147 | Isolate* isolate) { |
2148 | VMState<COMPILER> state(isolate); |
2149 | // Take ownership of compilation job. Deleting job also tears down the zone. |
2150 | std::unique_ptr<OptimizedCompilationJob> job_scope(job); |
2151 | OptimizedCompilationInfo* compilation_info = job->compilation_info(); |
2152 | |
2153 | TimerEventScope<TimerEventRecompileSynchronous> timer(isolate); |
2154 | RuntimeCallTimerScope runtimeTimer( |
2155 | isolate, RuntimeCallCounterId::kRecompileSynchronous); |
2156 | |
---|