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/compilation-cache.h"
11 : #include "src/compiler.h"
12 : #include "src/execution.h"
13 : #include "src/frames-inl.h"
14 : #include "src/global-handles.h"
15 : #include "src/interpreter/interpreter.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 : // Number of times a function has to be seen on the stack before it is
21 : // optimized.
22 : static const int kProfilerTicksBeforeOptimization = 2;
23 :
24 : // The number of ticks required for optimizing a function increases with
25 : // the size of the bytecode. This is in addition to the
26 : // kProfilerTicksBeforeOptimization required for any function.
27 : static const int kBytecodeSizeAllowancePerTick = 1200;
28 :
29 : // Maximum size in bytes of generate code for a function to allow OSR.
30 : static const int kOSRBytecodeSizeAllowanceBase = 180;
31 :
32 : static const int kOSRBytecodeSizeAllowancePerTick = 48;
33 :
34 : // Maximum size in bytes of generated code for a function to be optimized
35 : // the very first time it is seen on the stack.
36 : static const int kMaxBytecodeSizeForEarlyOpt = 90;
37 :
38 : // Certain functions are simply too big to be worth optimizing.
39 : static const int kMaxBytecodeSizeForOpt = 60 * KB;
40 :
41 : #define OPTIMIZATION_REASON_LIST(V) \
42 : V(DoNotOptimize, "do not optimize") \
43 : V(HotAndStable, "hot and stable") \
44 : V(SmallFunction, "small function")
45 :
46 : enum class OptimizationReason : uint8_t {
47 : #define OPTIMIZATION_REASON_CONSTANTS(Constant, message) k##Constant,
48 : OPTIMIZATION_REASON_LIST(OPTIMIZATION_REASON_CONSTANTS)
49 : #undef OPTIMIZATION_REASON_CONSTANTS
50 : };
51 :
52 0 : char const* OptimizationReasonToString(OptimizationReason reason) {
53 : static char const* reasons[] = {
54 : #define OPTIMIZATION_REASON_TEXTS(Constant, message) message,
55 : OPTIMIZATION_REASON_LIST(OPTIMIZATION_REASON_TEXTS)
56 : #undef OPTIMIZATION_REASON_TEXTS
57 : };
58 28594 : size_t const index = static_cast<size_t>(reason);
59 : DCHECK_LT(index, arraysize(reasons));
60 28594 : return reasons[index];
61 : }
62 :
63 0 : std::ostream& operator<<(std::ostream& os, OptimizationReason reason) {
64 0 : return os << OptimizationReasonToString(reason);
65 : }
66 :
67 61049 : RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
68 : : isolate_(isolate),
69 61049 : any_ic_changed_(false) {
70 61049 : }
71 :
72 1 : static void GetICCounts(JSFunction function, int* ic_with_type_info_count,
73 : int* ic_generic_count, int* ic_total_count,
74 : int* type_info_percentage, int* generic_percentage) {
75 1 : FeedbackVector vector = function->feedback_vector();
76 : vector->ComputeCounts(ic_with_type_info_count, ic_generic_count,
77 1 : ic_total_count);
78 :
79 1 : if (*ic_total_count > 0) {
80 1 : *type_info_percentage = 100 * *ic_with_type_info_count / *ic_total_count;
81 1 : *generic_percentage = 100 * *ic_generic_count / *ic_total_count;
82 : } else {
83 0 : *type_info_percentage = 100; // Compared against lower bound.
84 0 : *generic_percentage = 0; // Compared against upper bound.
85 : }
86 1 : }
87 :
88 28594 : static void TraceRecompile(JSFunction function, const char* reason,
89 : const char* type) {
90 28594 : if (FLAG_trace_opt) {
91 1 : PrintF("[marking ");
92 1 : function->ShortPrint();
93 1 : PrintF(" for %s recompilation, reason: %s", type, reason);
94 1 : if (FLAG_type_info_threshold > 0) {
95 : int typeinfo, generic, total, type_percentage, generic_percentage;
96 : GetICCounts(function, &typeinfo, &generic, &total, &type_percentage,
97 1 : &generic_percentage);
98 : PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total,
99 1 : type_percentage);
100 1 : PrintF(", generic ICs: %d/%d (%d%%)", generic, total, generic_percentage);
101 : }
102 1 : PrintF("]\n");
103 : }
104 28594 : }
105 :
106 28594 : void RuntimeProfiler::Optimize(JSFunction function, OptimizationReason reason) {
107 : DCHECK_NE(reason, OptimizationReason::kDoNotOptimize);
108 28594 : TraceRecompile(function, OptimizationReasonToString(reason), "optimized");
109 28594 : function->MarkForOptimization(ConcurrencyMode::kConcurrent);
110 28594 : }
111 :
112 36439 : void RuntimeProfiler::AttemptOnStackReplacement(InterpretedFrame* frame,
113 : int loop_nesting_levels) {
114 36439 : JSFunction function = frame->function();
115 36439 : SharedFunctionInfo shared = function->shared();
116 36439 : if (!FLAG_use_osr || !shared->IsUserJavaScript()) {
117 4783 : return;
118 : }
119 :
120 : // If the code is not optimizable, don't try OSR.
121 34942 : if (shared->optimization_disabled()) return;
122 :
123 : // We're using on-stack replacement: Store new loop nesting level in
124 : // BytecodeArray header so that certain back edges in any interpreter frame
125 : // for this bytecode will trigger on-stack replacement for that frame.
126 31656 : if (FLAG_trace_osr) {
127 0 : PrintF("[OSR - arming back edges in ");
128 0 : function->PrintName();
129 0 : PrintF("]\n");
130 : }
131 :
132 : DCHECK_EQ(StackFrame::INTERPRETED, frame->type());
133 63312 : int level = frame->GetBytecodeArray()->osr_loop_nesting_level();
134 : frame->GetBytecodeArray()->set_osr_loop_nesting_level(
135 94968 : Min(level + loop_nesting_levels, AbstractCode::kMaxLoopNestingMarker));
136 : }
137 :
138 152302 : void RuntimeProfiler::MaybeOptimize(JSFunction function,
139 : InterpretedFrame* frame) {
140 152302 : if (function->IsInOptimizationQueue()) {
141 2541 : if (FLAG_trace_opt_verbose) {
142 0 : PrintF("[function ");
143 0 : function->PrintName();
144 0 : PrintF(" is already in optimization queue]\n");
145 : }
146 : return;
147 : }
148 :
149 149761 : if (FLAG_always_osr) {
150 0 : AttemptOnStackReplacement(frame, AbstractCode::kMaxLoopNestingMarker);
151 : // Fall through and do a normal optimized compile as well.
152 149761 : } else if (MaybeOSR(function, frame)) {
153 : return;
154 : }
155 :
156 108562 : if (function->shared()->optimization_disabled()) return;
157 :
158 : OptimizationReason reason =
159 67406 : ShouldOptimize(function, function->shared()->GetBytecodeArray());
160 :
161 67406 : if (reason != OptimizationReason::kDoNotOptimize) {
162 28594 : Optimize(function, reason);
163 : }
164 : }
165 :
166 149761 : bool RuntimeProfiler::MaybeOSR(JSFunction function, InterpretedFrame* frame) {
167 299522 : int ticks = function->feedback_vector()->profiler_ticks();
168 : // TODO(rmcilroy): Also ensure we only OSR top-level code if it is smaller
169 : // than kMaxToplevelSourceSize.
170 :
171 447797 : if (function->IsMarkedForOptimization() ||
172 262611 : function->IsMarkedForConcurrentOptimization() ||
173 112850 : function->HasOptimizedCode()) {
174 : // Attempt OSR if we are still running interpreted code even though the
175 : // the function has long been marked or even already been optimized.
176 : int64_t allowance =
177 : kOSRBytecodeSizeAllowanceBase +
178 41199 : static_cast<int64_t>(ticks) * kOSRBytecodeSizeAllowancePerTick;
179 82398 : if (function->shared()->GetBytecodeArray()->length() <= allowance) {
180 29851 : AttemptOnStackReplacement(frame);
181 : }
182 : return true;
183 : }
184 : return false;
185 : }
186 :
187 67406 : OptimizationReason RuntimeProfiler::ShouldOptimize(JSFunction function,
188 : BytecodeArray bytecode) {
189 134812 : int ticks = function->feedback_vector()->profiler_ticks();
190 67406 : if (bytecode->length() > kMaxBytecodeSizeForOpt) {
191 : return OptimizationReason::kDoNotOptimize;
192 : }
193 :
194 : int ticks_for_optimization =
195 : kProfilerTicksBeforeOptimization +
196 65216 : (bytecode->length() / kBytecodeSizeAllowancePerTick);
197 65216 : if (ticks >= ticks_for_optimization) {
198 : return OptimizationReason::kHotAndStable;
199 82331 : } else if (!any_ic_changed_ &&
200 : bytecode->length() < kMaxBytecodeSizeForEarlyOpt) {
201 : // If no IC was patched since the last tick and this function is very
202 : // small, optimistically optimize it now.
203 : return OptimizationReason::kSmallFunction;
204 36622 : } else if (FLAG_trace_opt_verbose) {
205 0 : PrintF("[not yet optimizing ");
206 0 : function->PrintName();
207 : PrintF(", not enough ticks: %d/%d and ", ticks,
208 0 : kProfilerTicksBeforeOptimization);
209 0 : if (any_ic_changed_) {
210 0 : PrintF("ICs changed]\n");
211 : } else {
212 : PrintF(" too large for small function optimization: %d/%d]\n",
213 0 : bytecode->length(), kMaxBytecodeSizeForEarlyOpt);
214 : }
215 : }
216 : return OptimizationReason::kDoNotOptimize;
217 : }
218 :
219 242327 : void RuntimeProfiler::MarkCandidatesForOptimization() {
220 242327 : HandleScope scope(isolate_);
221 :
222 484654 : if (!isolate_->use_optimizer()) return;
223 :
224 : DisallowHeapAllocation no_gc;
225 :
226 : // Run through the JavaScript frames and collect them. If we already
227 : // have a sample of the function, we mark it for optimizations
228 : // (eagerly or lazily).
229 : int frame_count = 0;
230 159965 : int frame_count_limit = FLAG_frame_count;
231 479890 : for (JavaScriptFrameIterator it(isolate_);
232 479890 : frame_count++ < frame_count_limit && !it.done();
233 159960 : it.Advance()) {
234 : JavaScriptFrame* frame = it.frame();
235 327578 : if (!frame->is_interpreted()) continue;
236 :
237 152302 : JSFunction function = frame->function();
238 : DCHECK(function->shared()->is_compiled());
239 304604 : if (!function->shared()->IsInterpreted()) continue;
240 :
241 152302 : if (!function->has_feedback_vector()) continue;
242 :
243 152302 : MaybeOptimize(function, InterpretedFrame::cast(frame));
244 :
245 : // TODO(leszeks): Move this increment to before the maybe optimize checks,
246 : // and update the tests to assume the increment has already happened.
247 304604 : int ticks = function->feedback_vector()->profiler_ticks();
248 152302 : if (ticks < Smi::kMaxValue) {
249 304604 : function->feedback_vector()->set_profiler_ticks(ticks + 1);
250 : }
251 : }
252 159965 : any_ic_changed_ = false;
253 : }
254 :
255 : } // namespace internal
256 178779 : } // namespace v8
|