Line data Source code
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/runtime-profiler.h"
6 :
7 : #include "src/assembler.h"
8 : #include "src/base/platform/platform.h"
9 : #include "src/bootstrapper.h"
10 : #include "src/code-stubs.h"
11 : #include "src/compilation-cache.h"
12 : #include "src/compiler.h"
13 : #include "src/execution.h"
14 : #include "src/frames-inl.h"
15 : #include "src/full-codegen/full-codegen.h"
16 : #include "src/global-handles.h"
17 : #include "src/interpreter/interpreter.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 : // Number of times a function has to be seen on the stack before it is
23 : // optimized.
24 : static const int kProfilerTicksBeforeOptimization = 2;
25 : // If the function optimization was disabled due to high deoptimization count,
26 : // but the function is hot and has been seen on the stack this number of times,
27 : // then we try to reenable optimization for this function.
28 : static const int kProfilerTicksBeforeReenablingOptimization = 250;
29 : // If a function does not have enough type info (according to
30 : // FLAG_type_info_threshold), but has seen a huge number of ticks,
31 : // optimize it as it is.
32 : static const int kTicksWhenNotEnoughTypeInfo = 100;
33 : // We only have one byte to store the number of ticks.
34 : STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
35 : STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
36 : STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);
37 :
38 : // Maximum size in bytes of generate code for a function to allow OSR.
39 : static const int kOSRCodeSizeAllowanceBase =
40 : 100 * FullCodeGenerator::kCodeSizeMultiplier;
41 : static const int kOSRCodeSizeAllowanceBaseIgnition =
42 : 10 * interpreter::Interpreter::kCodeSizeMultiplier;
43 :
44 : static const int kOSRCodeSizeAllowancePerTick =
45 : 4 * FullCodeGenerator::kCodeSizeMultiplier;
46 : static const int kOSRCodeSizeAllowancePerTickIgnition =
47 : 2 * interpreter::Interpreter::kCodeSizeMultiplier;
48 :
49 : // Maximum size in bytes of generated code for a function to be optimized
50 : // the very first time it is seen on the stack.
51 : static const int kMaxSizeEarlyOpt =
52 : 5 * FullCodeGenerator::kCodeSizeMultiplier;
53 : static const int kMaxSizeEarlyOptIgnition =
54 : 5 * interpreter::Interpreter::kCodeSizeMultiplier;
55 :
56 : // Certain functions are simply too big to be worth optimizing.
57 : // We aren't using the code size multiplier here because there is no
58 : // "kMaxSizeOpt" with which we would need to normalize. This constant is
59 : // only for optimization decisions coming into TurboFan from Ignition.
60 : static const int kMaxSizeOptIgnition = 250 * 1024;
61 :
62 : #define OPTIMIZATION_REASON_LIST(V) \
63 : V(DoNotOptimize, "do not optimize") \
64 : V(HotAndStable, "hot and stable") \
65 : V(HotWithoutMuchTypeInfo, "not much type info but very hot") \
66 : V(SmallFunction, "small function")
67 :
68 : enum class OptimizationReason : uint8_t {
69 : #define OPTIMIZATION_REASON_CONSTANTS(Constant, message) k##Constant,
70 : OPTIMIZATION_REASON_LIST(OPTIMIZATION_REASON_CONSTANTS)
71 : #undef OPTIMIZATION_REASON_CONSTANTS
72 : };
73 :
74 0 : char const* OptimizationReasonToString(OptimizationReason reason) {
75 : static char const* reasons[] = {
76 : #define OPTIMIZATION_REASON_TEXTS(Constant, message) message,
77 : OPTIMIZATION_REASON_LIST(OPTIMIZATION_REASON_TEXTS)
78 : #undef OPTIMIZATION_REASON_TEXTS
79 : };
80 13475 : size_t const index = static_cast<size_t>(reason);
81 : DCHECK_LT(index, arraysize(reasons));
82 141012 : return reasons[index];
83 : }
84 :
85 0 : std::ostream& operator<<(std::ostream& os, OptimizationReason reason) {
86 0 : return os << OptimizationReasonToString(reason);
87 : }
88 :
89 60782 : RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
90 : : isolate_(isolate),
91 60782 : any_ic_changed_(false) {
92 60782 : }
93 :
94 179274 : static void GetICCounts(JSFunction* function, int* ic_with_type_info_count,
95 : int* ic_generic_count, int* ic_total_count,
96 : int* type_info_percentage, int* generic_percentage) {
97 179274 : *ic_total_count = 0;
98 179274 : *ic_generic_count = 0;
99 179274 : *ic_with_type_info_count = 0;
100 179274 : if (function->code()->kind() == Code::FUNCTION) {
101 : Code* shared_code = function->shared()->code();
102 : Object* raw_info = shared_code->type_feedback_info();
103 161719 : if (raw_info->IsTypeFeedbackInfo()) {
104 : TypeFeedbackInfo* info = TypeFeedbackInfo::cast(raw_info);
105 161719 : *ic_with_type_info_count = info->ic_with_type_info_count();
106 161719 : *ic_generic_count = info->ic_generic_count();
107 161719 : *ic_total_count = info->ic_total_count();
108 : }
109 : }
110 :
111 : // Harvest vector-ics as well
112 : FeedbackVector* vector = function->feedback_vector();
113 179274 : int with = 0, gen = 0, type_vector_ic_count = 0;
114 : const bool is_interpreted = function->shared()->IsInterpreted();
115 :
116 179274 : vector->ComputeCounts(&with, &gen, &type_vector_ic_count, is_interpreted);
117 179274 : *ic_total_count += type_vector_ic_count;
118 179274 : *ic_with_type_info_count += with;
119 179274 : *ic_generic_count += gen;
120 :
121 179274 : if (*ic_total_count > 0) {
122 173918 : *type_info_percentage = 100 * *ic_with_type_info_count / *ic_total_count;
123 173918 : *generic_percentage = 100 * *ic_generic_count / *ic_total_count;
124 : } else {
125 5356 : *type_info_percentage = 100; // Compared against lower bound.
126 5356 : *generic_percentage = 0; // Compared against upper bound.
127 : }
128 179274 : }
129 :
130 141012 : static void TraceRecompile(JSFunction* function, const char* reason,
131 : const char* type) {
132 141012 : if (FLAG_trace_opt &&
133 0 : function->shared()->PassesFilter(FLAG_hydrogen_filter)) {
134 0 : PrintF("[marking ");
135 0 : function->ShortPrint();
136 0 : PrintF(" for %s recompilation, reason: %s", type, reason);
137 0 : if (FLAG_type_info_threshold > 0) {
138 : int typeinfo, generic, total, type_percentage, generic_percentage;
139 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
140 0 : &generic_percentage);
141 : PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total,
142 0 : type_percentage);
143 0 : PrintF(", generic ICs: %d/%d (%d%%)", generic, total, generic_percentage);
144 : }
145 0 : PrintF("]\n");
146 : }
147 141012 : }
148 :
149 0 : void RuntimeProfiler::Optimize(JSFunction* function,
150 : OptimizationReason reason) {
151 : DCHECK_NE(reason, OptimizationReason::kDoNotOptimize);
152 141012 : TraceRecompile(function, OptimizationReasonToString(reason), "optimized");
153 141012 : function->AttemptConcurrentOptimization();
154 0 : }
155 :
156 27700 : void RuntimeProfiler::AttemptOnStackReplacement(JavaScriptFrame* frame,
157 : int loop_nesting_levels) {
158 27700 : JSFunction* function = frame->function();
159 : SharedFunctionInfo* shared = function->shared();
160 27700 : if (!FLAG_use_osr || !function->shared()->IsUserJavaScript()) {
161 : return;
162 : }
163 :
164 : // If the code is not optimizable, don't try OSR.
165 16079 : if (shared->optimization_disabled()) return;
166 :
167 : // We are not prepared to do OSR for a function that already has an
168 : // allocated arguments object. The optimized code would bypass it for
169 : // arguments accesses, which is unsound. Don't try OSR.
170 13287 : if (shared->uses_arguments()) return;
171 :
172 : // We're using on-stack replacement: modify unoptimized code so that
173 : // certain back edges in any unoptimized frame will trigger on-stack
174 : // replacement for that frame.
175 : // - Ignition: Store new loop nesting level in BytecodeArray header.
176 : // - FullCodegen: Patch back edges up to new level using BackEdgeTable.
177 12429 : if (FLAG_trace_osr) {
178 0 : PrintF("[OSR - arming back edges in ");
179 0 : function->PrintName();
180 0 : PrintF("]\n");
181 : }
182 :
183 12429 : if (frame->type() == StackFrame::JAVA_SCRIPT) {
184 : DCHECK(shared->HasBaselineCode());
185 : DCHECK(BackEdgeTable::Verify(shared->GetIsolate(), shared->code()));
186 10638 : for (int i = 0; i < loop_nesting_levels; i++) {
187 10638 : BackEdgeTable::Patch(isolate_, shared->code());
188 : }
189 8431 : } else if (frame->type() == StackFrame::INTERPRETED) {
190 : DCHECK(shared->HasBytecodeArray());
191 8431 : if (!FLAG_ignition_osr) return; // Only use this when enabled.
192 : int level = shared->bytecode_array()->osr_loop_nesting_level();
193 : shared->bytecode_array()->set_osr_loop_nesting_level(
194 8431 : Min(level + loop_nesting_levels, AbstractCode::kMaxLoopNestingMarker));
195 : } else {
196 0 : UNREACHABLE();
197 : }
198 : }
199 :
200 404756 : void RuntimeProfiler::MaybeOptimizeFullCodegen(JSFunction* function,
201 : JavaScriptFrame* frame,
202 : int frame_count) {
203 : SharedFunctionInfo* shared = function->shared();
204 : Code* shared_code = shared->code();
205 404756 : if (shared_code->kind() != Code::FUNCTION) return;
206 404672 : if (function->IsInOptimizationQueue()) {
207 69261 : if (FLAG_trace_opt_verbose) {
208 0 : PrintF("[function ");
209 0 : function->PrintName();
210 0 : PrintF(" is already in optimization queue]\n");
211 : }
212 : return;
213 : }
214 :
215 335411 : if (FLAG_always_osr) {
216 0 : AttemptOnStackReplacement(frame, AbstractCode::kMaxLoopNestingMarker);
217 : // Fall through and do a normal optimized compile as well.
218 1272248 : } else if (!frame->is_optimized() &&
219 266007 : (function->IsMarkedForOptimization() ||
220 260865 : function->IsMarkedForConcurrentOptimization() ||
221 : function->IsOptimized())) {
222 : // Attempt OSR if we are still running unoptimized code even though the
223 : // the function has long been marked or even already been optimized.
224 : int ticks = shared_code->profiler_ticks();
225 : int64_t allowance =
226 : kOSRCodeSizeAllowanceBase +
227 5722 : static_cast<int64_t>(ticks) * kOSRCodeSizeAllowancePerTick;
228 5722 : if (shared_code->CodeSize() > allowance &&
229 : ticks < Code::ProfilerTicksField::kMax) {
230 1156 : shared_code->set_profiler_ticks(ticks + 1);
231 : } else {
232 4566 : AttemptOnStackReplacement(frame);
233 : }
234 : return;
235 : }
236 :
237 : // Only record top-level code on top of the execution stack and
238 : // avoid optimizing excessively large scripts since top-level code
239 : // will be executed only once.
240 : const int kMaxToplevelSourceSize = 10 * 1024;
241 659378 : if (shared->is_toplevel() &&
242 8653 : (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
243 : return;
244 : }
245 :
246 : // Do not record non-optimizable functions.
247 329450 : if (shared->optimization_disabled()) {
248 50776 : if (shared->deopt_count() >= FLAG_max_deopt_count) {
249 : // If optimization was disabled due to many deoptimizations,
250 : // then check if the function is hot and try to reenable optimization.
251 : int ticks = shared_code->profiler_ticks();
252 19790 : if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
253 : shared_code->set_profiler_ticks(0);
254 44 : shared->TryReenableOptimization();
255 : } else {
256 19746 : shared_code->set_profiler_ticks(ticks + 1);
257 : }
258 : }
259 : return;
260 : }
261 557348 : if (frame->is_optimized()) return;
262 :
263 : int ticks = shared_code->profiler_ticks();
264 :
265 209352 : if (ticks >= kProfilerTicksBeforeOptimization) {
266 : int typeinfo, generic, total, type_percentage, generic_percentage;
267 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
268 41985 : &generic_percentage);
269 53789 : if (type_percentage >= FLAG_type_info_threshold &&
270 11804 : generic_percentage <= FLAG_generic_ic_threshold) {
271 : // If this particular function hasn't had any ICs patched for enough
272 : // ticks, optimize it now.
273 : Optimize(function, OptimizationReason::kHotAndStable);
274 32439 : } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
275 : Optimize(function, OptimizationReason::kHotWithoutMuchTypeInfo);
276 : } else {
277 32284 : shared_code->set_profiler_ticks(ticks + 1);
278 32284 : if (FLAG_trace_opt_verbose) {
279 0 : PrintF("[not yet optimizing ");
280 0 : function->PrintName();
281 : PrintF(", not enough type info: %d/%d (%d%%)]\n", typeinfo, total,
282 0 : type_percentage);
283 : }
284 : }
285 301459 : } else if (!any_ic_changed_ &&
286 : shared_code->instruction_size() < kMaxSizeEarlyOpt) {
287 : // If no IC was patched since the last tick and this function is very
288 : // small, optimistically optimize it now.
289 : int typeinfo, generic, total, type_percentage, generic_percentage;
290 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
291 119736 : &generic_percentage);
292 237805 : if (type_percentage >= FLAG_type_info_threshold &&
293 118069 : generic_percentage <= FLAG_generic_ic_threshold) {
294 : Optimize(function, OptimizationReason::kSmallFunction);
295 : } else {
296 1900 : shared_code->set_profiler_ticks(ticks + 1);
297 : }
298 : } else {
299 47631 : shared_code->set_profiler_ticks(ticks + 1);
300 : }
301 : }
302 :
303 205207 : void RuntimeProfiler::MaybeOptimizeIgnition(JSFunction* function,
304 : JavaScriptFrame* frame) {
305 205207 : if (function->IsInOptimizationQueue()) {
306 8500 : if (FLAG_trace_opt_verbose) {
307 0 : PrintF("[function ");
308 0 : function->PrintName();
309 0 : PrintF(" is already in optimization queue]\n");
310 : }
311 : return;
312 : }
313 :
314 196707 : if (FLAG_always_osr) {
315 0 : AttemptOnStackReplacement(frame, AbstractCode::kMaxLoopNestingMarker);
316 : // Fall through and do a normal optimized compile as well.
317 196707 : } else if (MaybeOSRIgnition(function, frame)) {
318 : return;
319 : }
320 :
321 : SharedFunctionInfo* shared = function->shared();
322 : int ticks = shared->profiler_ticks();
323 :
324 179682 : if (shared->optimization_disabled()) {
325 25272 : if (shared->deopt_count() >= FLAG_max_deopt_count) {
326 : // If optimization was disabled due to many deoptimizations,
327 : // then check if the function is hot and try to reenable optimization.
328 6522 : if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
329 : shared->set_profiler_ticks(0);
330 0 : shared->TryReenableOptimization();
331 : }
332 : }
333 : return;
334 : }
335 :
336 308820 : if (frame->is_optimized()) return;
337 :
338 47825 : OptimizationReason reason = ShouldOptimizeIgnition(function, frame);
339 :
340 47825 : if (reason != OptimizationReason::kDoNotOptimize) {
341 : Optimize(function, reason);
342 : }
343 : }
344 :
345 196707 : bool RuntimeProfiler::MaybeOSRIgnition(JSFunction* function,
346 : JavaScriptFrame* frame) {
347 : SharedFunctionInfo* shared = function->shared();
348 : int ticks = shared->profiler_ticks();
349 :
350 : // TODO(rmcilroy): Also ensure we only OSR top-level code if it is smaller
351 : // than kMaxToplevelSourceSize.
352 :
353 680243 : if (!frame->is_optimized() &&
354 89173 : (function->IsMarkedForOptimization() ||
355 75546 : function->IsMarkedForConcurrentOptimization() ||
356 : function->IsOptimized())) {
357 : // Attempt OSR if we are still running interpreted code even though the
358 : // the function has long been marked or even already been optimized.
359 : int64_t allowance =
360 : kOSRCodeSizeAllowanceBaseIgnition +
361 17025 : static_cast<int64_t>(ticks) * kOSRCodeSizeAllowancePerTickIgnition;
362 17025 : if (shared->bytecode_array()->Size() <= allowance) {
363 8335 : AttemptOnStackReplacement(frame);
364 : }
365 : return true;
366 : }
367 : return false;
368 : }
369 :
370 47825 : OptimizationReason RuntimeProfiler::ShouldOptimizeIgnition(
371 : JSFunction* function, JavaScriptFrame* frame) {
372 : SharedFunctionInfo* shared = function->shared();
373 : int ticks = shared->profiler_ticks();
374 :
375 47825 : if (shared->bytecode_array()->Size() > kMaxSizeOptIgnition) {
376 : return OptimizationReason::kDoNotOptimize;
377 : }
378 :
379 47806 : if (ticks >= kProfilerTicksBeforeOptimization) {
380 : int typeinfo, generic, total, type_percentage, generic_percentage;
381 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
382 12325 : &generic_percentage);
383 12325 : if (type_percentage >= FLAG_type_info_threshold) {
384 : // If this particular function hasn't had any ICs patched for enough
385 : // ticks, optimize it now.
386 : return OptimizationReason::kHotAndStable;
387 3885 : } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
388 : return OptimizationReason::kHotWithoutMuchTypeInfo;
389 : } else {
390 3868 : if (FLAG_trace_opt_verbose) {
391 0 : PrintF("[not yet optimizing ");
392 0 : function->PrintName();
393 : PrintF(", not enough type info: %d/%d (%d%%)]\n", typeinfo, total,
394 0 : type_percentage);
395 : }
396 : return OptimizationReason::kDoNotOptimize;
397 : }
398 56591 : } else if (!any_ic_changed_ &&
399 21110 : shared->bytecode_array()->Size() < kMaxSizeEarlyOptIgnition) {
400 : // If no IC was patched since the last tick and this function is very
401 : // small, optimistically optimize it now.
402 : int typeinfo, generic, total, type_percentage, generic_percentage;
403 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
404 5228 : &generic_percentage);
405 5228 : if (type_percentage < FLAG_type_info_threshold) {
406 210 : if (FLAG_trace_opt_verbose) {
407 0 : PrintF("[not yet optimizing ");
408 0 : function->PrintName();
409 : PrintF(
410 : ", not enough type info for small function optimization: %d/%d "
411 : "(%d%%)]\n",
412 0 : typeinfo, total, type_percentage);
413 : }
414 : return OptimizationReason::kDoNotOptimize;
415 : }
416 : return OptimizationReason::kSmallFunction;
417 30253 : } else if (FLAG_trace_opt_verbose) {
418 0 : PrintF("[not yet optimizing ");
419 0 : function->PrintName();
420 : PrintF(", not enough ticks: %d/%d and ", ticks,
421 0 : kProfilerTicksBeforeOptimization);
422 0 : if (any_ic_changed_) {
423 0 : PrintF("ICs changed]\n");
424 : } else {
425 : PrintF(" too large for small function optimization: %d/%d]\n",
426 0 : shared->bytecode_array()->Size(), kMaxSizeEarlyOptIgnition);
427 : }
428 : }
429 : return OptimizationReason::kDoNotOptimize;
430 : }
431 :
432 716002 : void RuntimeProfiler::MarkCandidatesForOptimization() {
433 716002 : HandleScope scope(isolate_);
434 :
435 1432004 : if (!isolate_->use_crankshaft()) return;
436 :
437 : DisallowHeapAllocation no_gc;
438 :
439 : // Run through the JavaScript frames and collect them. If we already
440 : // have a sample of the function, we mark it for optimizations
441 : // (eagerly or lazily).
442 : int frame_count = 0;
443 609969 : int frame_count_limit = FLAG_frame_count;
444 1829901 : for (JavaScriptFrameIterator it(isolate_);
445 1829901 : frame_count++ < frame_count_limit && !it.done();
446 609963 : it.Advance()) {
447 : JavaScriptFrame* frame = it.frame();
448 609963 : JSFunction* function = frame->function();
449 :
450 609963 : if (function->shared()->IsInterpreted()) {
451 205207 : MaybeOptimizeIgnition(function, frame);
452 : } else {
453 404756 : MaybeOptimizeFullCodegen(function, frame, frame_count);
454 : }
455 :
456 : // Update shared function info ticks after checking for whether functions
457 : // should be optimized to keep FCG (which updates ticks on code) and
458 : // Ignition (which updates ticks on shared function info) in sync.
459 : List<SharedFunctionInfo*> functions(4);
460 609963 : frame->GetFunctions(&functions);
461 1830025 : for (int i = functions.length(); --i >= 0;) {
462 1220198 : SharedFunctionInfo* shared_function_info = functions[i];
463 : int ticks = shared_function_info->profiler_ticks();
464 610099 : if (ticks < Smi::kMaxValue) {
465 610099 : shared_function_info->set_profiler_ticks(ticks + 1);
466 : }
467 : }
468 : }
469 609969 : any_ic_changed_ = false;
470 : }
471 :
472 : } // namespace internal
473 : } // namespace v8
|