Line data Source code
1 : // Copyright 2012 the V8 project authors. All rights reserved.
2 : // Redistribution and use in source and binary forms, with or without
3 : // modification, are permitted provided that the following conditions are
4 : // met:
5 : //
6 : // * Redistributions of source code must retain the above copyright
7 : // notice, this list of conditions and the following disclaimer.
8 : // * Redistributions in binary form must reproduce the above
9 : // copyright notice, this list of conditions and the following
10 : // disclaimer in the documentation and/or other materials provided
11 : // with the distribution.
12 : // * Neither the name of Google Inc. nor the names of its
13 : // contributors may be used to endorse or promote products derived
14 : // from this software without specific prior written permission.
15 : //
16 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 :
28 : #include <stdlib.h>
29 :
30 : #include "src/v8.h"
31 :
32 : #include "src/api-inl.h"
33 : #include "src/base/platform/platform.h"
34 : #include "src/compilation-cache.h"
35 : #include "src/debug/debug.h"
36 : #include "src/deoptimizer.h"
37 : #include "src/isolate.h"
38 : #include "src/objects-inl.h"
39 : #include "test/cctest/cctest.h"
40 :
41 : using ::v8::base::OS;
42 : using ::v8::internal::Deoptimizer;
43 : using ::v8::internal::EmbeddedVector;
44 : using ::v8::internal::Handle;
45 : using ::v8::internal::JSFunction;
46 :
47 : // Size of temp buffer for formatting small strings.
48 : #define SMALL_STRING_BUFFER_SIZE 80
49 :
50 : // Utility class to set the following runtime flags when constructed and return
51 : // to their default state when destroyed:
52 : // --allow-natives-syntax --always-opt --noturbo-inlining
53 : class AlwaysOptimizeAllowNativesSyntaxNoInlining {
54 : public:
55 : AlwaysOptimizeAllowNativesSyntaxNoInlining()
56 : : always_opt_(i::FLAG_always_opt),
57 : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
58 50 : turbo_inlining_(i::FLAG_turbo_inlining) {
59 50 : i::FLAG_always_opt = true;
60 50 : i::FLAG_allow_natives_syntax = true;
61 50 : i::FLAG_turbo_inlining = false;
62 : }
63 :
64 : ~AlwaysOptimizeAllowNativesSyntaxNoInlining() {
65 50 : i::FLAG_always_opt = always_opt_;
66 50 : i::FLAG_allow_natives_syntax = allow_natives_syntax_;
67 50 : i::FLAG_turbo_inlining = turbo_inlining_;
68 : }
69 :
70 : private:
71 : bool always_opt_;
72 : bool allow_natives_syntax_;
73 : bool turbo_inlining_;
74 : };
75 :
76 : // Utility class to set the following runtime flags when constructed and return
77 : // to their default state when destroyed:
78 : // --allow-natives-syntax --noturbo-inlining
79 : class AllowNativesSyntaxNoInlining {
80 : public:
81 : AllowNativesSyntaxNoInlining()
82 : : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
83 45 : turbo_inlining_(i::FLAG_turbo_inlining) {
84 45 : i::FLAG_allow_natives_syntax = true;
85 45 : i::FLAG_turbo_inlining = false;
86 : }
87 :
88 : ~AllowNativesSyntaxNoInlining() {
89 45 : i::FLAG_allow_natives_syntax = allow_natives_syntax_;
90 45 : i::FLAG_turbo_inlining = turbo_inlining_;
91 : }
92 :
93 : private:
94 : bool allow_natives_syntax_;
95 : bool turbo_inlining_;
96 : };
97 :
98 160 : static Handle<JSFunction> GetJSFunction(v8::Local<v8::Context> context,
99 : const char* property_name) {
100 : v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
101 640 : context->Global()->Get(context, v8_str(property_name)).ToLocalChecked());
102 160 : return i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*fun));
103 : }
104 :
105 :
106 26644 : TEST(DeoptimizeSimple) {
107 : ManualGCScope manual_gc_scope;
108 5 : LocalContext env;
109 10 : v8::HandleScope scope(env->GetIsolate());
110 :
111 : // Test lazy deoptimization of a simple function.
112 : {
113 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
114 : CompileRun(
115 : "var count = 0;"
116 : "function h() { %DeoptimizeFunction(f); }"
117 : "function g() { count++; h(); }"
118 : "function f() { g(); };"
119 : "f();");
120 : }
121 5 : CcTest::CollectAllGarbage();
122 :
123 25 : CHECK_EQ(1, env->Global()
124 : ->Get(env.local(), v8_str("count"))
125 : .ToLocalChecked()
126 : ->Int32Value(env.local())
127 : .FromJust());
128 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
129 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
130 :
131 : // Test lazy deoptimization of a simple function. Call the function after the
132 : // deoptimization while it is still activated further down the stack.
133 : {
134 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
135 : CompileRun(
136 : "var count = 0;"
137 : "function g() { count++; %DeoptimizeFunction(f); f(false); }"
138 : "function f(x) { if (x) { g(); } else { return } };"
139 : "f(true);");
140 : }
141 5 : CcTest::CollectAllGarbage();
142 :
143 25 : CHECK_EQ(1, env->Global()
144 : ->Get(env.local(), v8_str("count"))
145 : .ToLocalChecked()
146 : ->Int32Value(env.local())
147 : .FromJust());
148 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
149 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
150 5 : }
151 :
152 :
153 26644 : TEST(DeoptimizeSimpleWithArguments) {
154 : ManualGCScope manual_gc_scope;
155 5 : LocalContext env;
156 10 : v8::HandleScope scope(env->GetIsolate());
157 :
158 : // Test lazy deoptimization of a simple function with some arguments.
159 : {
160 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
161 : CompileRun(
162 : "var count = 0;"
163 : "function h(x) { %DeoptimizeFunction(f); }"
164 : "function g(x, y) { count++; h(x); }"
165 : "function f(x, y, z) { g(1,x); y+z; };"
166 : "f(1, \"2\", false);");
167 : }
168 5 : CcTest::CollectAllGarbage();
169 :
170 25 : CHECK_EQ(1, env->Global()
171 : ->Get(env.local(), v8_str("count"))
172 : .ToLocalChecked()
173 : ->Int32Value(env.local())
174 : .FromJust());
175 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
176 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
177 :
178 : // Test lazy deoptimization of a simple function with some arguments. Call the
179 : // function after the deoptimization while it is still activated further down
180 : // the stack.
181 : {
182 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
183 : CompileRun(
184 : "var count = 0;"
185 : "function g(x, y) { count++; %DeoptimizeFunction(f); f(false, 1, y); }"
186 : "function f(x, y, z) { if (x) { g(x, y); } else { return y + z; } };"
187 : "f(true, 1, \"2\");");
188 : }
189 5 : CcTest::CollectAllGarbage();
190 :
191 25 : CHECK_EQ(1, env->Global()
192 : ->Get(env.local(), v8_str("count"))
193 : .ToLocalChecked()
194 : ->Int32Value(env.local())
195 : .FromJust());
196 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
197 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
198 5 : }
199 :
200 :
201 26644 : TEST(DeoptimizeSimpleNested) {
202 : ManualGCScope manual_gc_scope;
203 5 : LocalContext env;
204 10 : v8::HandleScope scope(env->GetIsolate());
205 :
206 : // Test lazy deoptimization of a simple function. Have a nested function call
207 : // do the deoptimization.
208 : {
209 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
210 : CompileRun(
211 : "var count = 0;"
212 : "var result = 0;"
213 : "function h(x, y, z) { return x + y + z; }"
214 : "function g(z) { count++; %DeoptimizeFunction(f); return z;}"
215 : "function f(x,y,z) { return h(x, y, g(z)); };"
216 : "result = f(1, 2, 3);");
217 5 : CcTest::CollectAllGarbage();
218 :
219 25 : CHECK_EQ(1, env->Global()
220 : ->Get(env.local(), v8_str("count"))
221 : .ToLocalChecked()
222 : ->Int32Value(env.local())
223 : .FromJust());
224 25 : CHECK_EQ(6, env->Global()
225 : ->Get(env.local(), v8_str("result"))
226 : .ToLocalChecked()
227 : ->Int32Value(env.local())
228 : .FromJust());
229 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
230 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
231 : }
232 5 : }
233 :
234 :
235 26644 : TEST(DeoptimizeRecursive) {
236 : ManualGCScope manual_gc_scope;
237 5 : LocalContext env;
238 10 : v8::HandleScope scope(env->GetIsolate());
239 :
240 : {
241 : // Test lazy deoptimization of a simple function called recursively. Call
242 : // the function recursively a number of times before deoptimizing it.
243 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
244 : CompileRun(
245 : "var count = 0;"
246 : "var calls = 0;"
247 : "function g() { count++; %DeoptimizeFunction(f); }"
248 : "function f(x) { calls++; if (x > 0) { f(x - 1); } else { g(); } };"
249 : "f(10);");
250 : }
251 5 : CcTest::CollectAllGarbage();
252 :
253 25 : CHECK_EQ(1, env->Global()
254 : ->Get(env.local(), v8_str("count"))
255 : .ToLocalChecked()
256 : ->Int32Value(env.local())
257 : .FromJust());
258 25 : CHECK_EQ(11, env->Global()
259 : ->Get(env.local(), v8_str("calls"))
260 : .ToLocalChecked()
261 : ->Int32Value(env.local())
262 : .FromJust());
263 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
264 :
265 : v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
266 10 : env->Global()
267 15 : ->Get(env.local(), v8_str(CcTest::isolate(), "f"))
268 : .ToLocalChecked());
269 5 : CHECK(!fun.IsEmpty());
270 5 : }
271 :
272 :
273 26644 : TEST(DeoptimizeMultiple) {
274 : ManualGCScope manual_gc_scope;
275 5 : LocalContext env;
276 10 : v8::HandleScope scope(env->GetIsolate());
277 :
278 : {
279 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
280 : CompileRun(
281 : "var count = 0;"
282 : "var result = 0;"
283 : "function g() { count++;"
284 : " %DeoptimizeFunction(f1);"
285 : " %DeoptimizeFunction(f2);"
286 : " %DeoptimizeFunction(f3);"
287 : " %DeoptimizeFunction(f4);}"
288 : "function f4(x) { g(); };"
289 : "function f3(x, y, z) { f4(); return x + y + z; };"
290 : "function f2(x, y) { return x + f3(y + 1, y + 1, y + 1) + y; };"
291 : "function f1(x) { return f2(x + 1, x + 1) + x; };"
292 : "result = f1(1);");
293 : }
294 5 : CcTest::CollectAllGarbage();
295 :
296 25 : CHECK_EQ(1, env->Global()
297 : ->Get(env.local(), v8_str("count"))
298 : .ToLocalChecked()
299 : ->Int32Value(env.local())
300 : .FromJust());
301 25 : CHECK_EQ(14, env->Global()
302 : ->Get(env.local(), v8_str("result"))
303 : .ToLocalChecked()
304 : ->Int32Value(env.local())
305 : .FromJust());
306 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
307 5 : }
308 :
309 :
310 26644 : TEST(DeoptimizeConstructor) {
311 : ManualGCScope manual_gc_scope;
312 5 : LocalContext env;
313 10 : v8::HandleScope scope(env->GetIsolate());
314 :
315 : {
316 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
317 : CompileRun(
318 : "var count = 0;"
319 : "function g() { count++;"
320 : " %DeoptimizeFunction(f); }"
321 : "function f() { g(); };"
322 : "result = new f() instanceof f;");
323 : }
324 5 : CcTest::CollectAllGarbage();
325 :
326 25 : CHECK_EQ(1, env->Global()
327 : ->Get(env.local(), v8_str("count"))
328 : .ToLocalChecked()
329 : ->Int32Value(env.local())
330 : .FromJust());
331 20 : CHECK(env->Global()
332 : ->Get(env.local(), v8_str("result"))
333 : .ToLocalChecked()
334 : ->IsTrue());
335 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
336 :
337 : {
338 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
339 : CompileRun(
340 : "var count = 0;"
341 : "var result = 0;"
342 : "function g() { count++;"
343 : " %DeoptimizeFunction(f); }"
344 : "function f(x, y) { this.x = x; g(); this.y = y; };"
345 : "result = new f(1, 2);"
346 : "result = result.x + result.y;");
347 : }
348 5 : CcTest::CollectAllGarbage();
349 :
350 25 : CHECK_EQ(1, env->Global()
351 : ->Get(env.local(), v8_str("count"))
352 : .ToLocalChecked()
353 : ->Int32Value(env.local())
354 : .FromJust());
355 25 : CHECK_EQ(3, env->Global()
356 : ->Get(env.local(), v8_str("result"))
357 : .ToLocalChecked()
358 : ->Int32Value(env.local())
359 : .FromJust());
360 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
361 5 : }
362 :
363 :
364 26644 : TEST(DeoptimizeConstructorMultiple) {
365 : ManualGCScope manual_gc_scope;
366 5 : LocalContext env;
367 10 : v8::HandleScope scope(env->GetIsolate());
368 :
369 : {
370 : AlwaysOptimizeAllowNativesSyntaxNoInlining options;
371 : CompileRun(
372 : "var count = 0;"
373 : "var result = 0;"
374 : "function g() { count++;"
375 : " %DeoptimizeFunction(f1);"
376 : " %DeoptimizeFunction(f2);"
377 : " %DeoptimizeFunction(f3);"
378 : " %DeoptimizeFunction(f4);}"
379 : "function f4(x) { this.result = x; g(); };"
380 : "function f3(x, y, z) { this.result = new f4(x + y + z).result; };"
381 : "function f2(x, y) {"
382 : " this.result = x + new f3(y + 1, y + 1, y + 1).result + y; };"
383 : "function f1(x) { this.result = new f2(x + 1, x + 1).result + x; };"
384 : "result = new f1(1).result;");
385 : }
386 5 : CcTest::CollectAllGarbage();
387 :
388 25 : CHECK_EQ(1, env->Global()
389 : ->Get(env.local(), v8_str("count"))
390 : .ToLocalChecked()
391 : ->Int32Value(env.local())
392 : .FromJust());
393 25 : CHECK_EQ(14, env->Global()
394 : ->Get(env.local(), v8_str("result"))
395 : .ToLocalChecked()
396 : ->Int32Value(env.local())
397 : .FromJust());
398 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
399 5 : }
400 :
401 :
402 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationADDString) {
403 : ManualGCScope manual_gc_scope;
404 5 : i::FLAG_concurrent_recompilation = false;
405 : AllowNativesSyntaxNoInlining options;
406 : v8::Isolate::CreateParams create_params;
407 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
408 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
409 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
410 5 : isolate->Enter();
411 : {
412 5 : LocalContext env(isolate);
413 10 : v8::HandleScope scope(env->GetIsolate());
414 :
415 : const char* f_source = "function f(x, y) { return x + y; };";
416 :
417 : {
418 : // Compile function f and collect to type feedback to insert binary op
419 : // stub call in the optimized code.
420 5 : i::FLAG_prepare_always_opt = true;
421 : CompileRun(
422 : "var count = 0;"
423 : "var result = 0;"
424 : "var deopt = false;"
425 : "function X() { };"
426 : "X.prototype.toString = function () {"
427 : " if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'"
428 : "};");
429 : CompileRun(f_source);
430 : CompileRun(
431 : "for (var i = 0; i < 5; i++) {"
432 : " f('a+', new X());"
433 : "};");
434 :
435 : // Compile an optimized version of f.
436 5 : i::FLAG_always_opt = true;
437 : CompileRun(f_source);
438 : CompileRun("f('a+', new X());");
439 13 : CHECK(!i_isolate->use_optimizer() ||
440 : GetJSFunction(env.local(), "f")->IsOptimized());
441 :
442 : // Call f and force deoptimization while processing the binary operation.
443 : CompileRun(
444 : "deopt = true;"
445 : "var result = f('a+', new X());");
446 : }
447 5 : CcTest::CollectAllGarbage(i_isolate);
448 :
449 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
450 25 : CHECK_EQ(1, env->Global()
451 : ->Get(env.local(), v8_str("count"))
452 : .ToLocalChecked()
453 : ->Int32Value(env.local())
454 : .FromJust());
455 : v8::Local<v8::Value> result =
456 20 : env->Global()->Get(env.local(), v8_str("result")).ToLocalChecked();
457 5 : CHECK(result->IsString());
458 10 : v8::String::Utf8Value utf8(isolate, result);
459 5 : CHECK_EQ(0, strcmp("a+an X", *utf8));
460 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
461 : }
462 5 : isolate->Exit();
463 5 : isolate->Dispose();
464 5 : }
465 :
466 :
467 25 : static void CompileConstructorWithDeoptimizingValueOf() {
468 : CompileRun("var count = 0;"
469 : "var result = 0;"
470 : "var deopt = false;"
471 : "function X() { };"
472 : "X.prototype.valueOf = function () {"
473 : " if (deopt) { count++; %DeoptimizeFunction(f); } return 8"
474 : "};");
475 25 : }
476 :
477 :
478 25 : static void TestDeoptimizeBinaryOpHelper(LocalContext* env,
479 : const char* binary_op) {
480 25 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>((*env)->GetIsolate());
481 : EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer;
482 : SNPrintF(f_source_buffer,
483 : "function f(x, y) { return x %s y; };",
484 25 : binary_op);
485 : char* f_source = f_source_buffer.start();
486 :
487 : AllowNativesSyntaxNoInlining options;
488 : // Compile function f and collect to type feedback to insert binary op stub
489 : // call in the optimized code.
490 25 : i::FLAG_prepare_always_opt = true;
491 25 : CompileConstructorWithDeoptimizingValueOf();
492 : CompileRun(f_source);
493 : CompileRun("for (var i = 0; i < 5; i++) {"
494 : " f(8, new X());"
495 : "};");
496 :
497 : // Compile an optimized version of f.
498 25 : i::FLAG_always_opt = true;
499 : CompileRun(f_source);
500 : CompileRun("f(7, new X());");
501 65 : CHECK(!i_isolate->use_optimizer() ||
502 : GetJSFunction((*env).local(), "f")->IsOptimized());
503 :
504 : // Call f and force deoptimization while processing the binary operation.
505 : CompileRun("deopt = true;"
506 : "var result = f(7, new X());");
507 25 : CcTest::CollectAllGarbage(i_isolate);
508 50 : CHECK(!GetJSFunction((*env).local(), "f")->IsOptimized());
509 25 : }
510 :
511 :
512 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationADD) {
513 : ManualGCScope manual_gc_scope;
514 5 : i::FLAG_concurrent_recompilation = false;
515 : v8::Isolate::CreateParams create_params;
516 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
517 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
518 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
519 5 : isolate->Enter();
520 : {
521 5 : LocalContext env(isolate);
522 10 : v8::HandleScope scope(env->GetIsolate());
523 :
524 5 : TestDeoptimizeBinaryOpHelper(&env, "+");
525 :
526 25 : CHECK_EQ(1, env->Global()
527 : ->Get(env.local(), v8_str("count"))
528 : .ToLocalChecked()
529 : ->Int32Value(env.local())
530 : .FromJust());
531 25 : CHECK_EQ(15, env->Global()
532 : ->Get(env.local(), v8_str("result"))
533 : .ToLocalChecked()
534 : ->Int32Value(env.local())
535 : .FromJust());
536 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
537 : }
538 5 : isolate->Exit();
539 5 : isolate->Dispose();
540 5 : }
541 :
542 :
543 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationSUB) {
544 : ManualGCScope manual_gc_scope;
545 5 : i::FLAG_concurrent_recompilation = false;
546 : v8::Isolate::CreateParams create_params;
547 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
548 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
549 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
550 5 : isolate->Enter();
551 : {
552 5 : LocalContext env(isolate);
553 10 : v8::HandleScope scope(env->GetIsolate());
554 :
555 5 : TestDeoptimizeBinaryOpHelper(&env, "-");
556 :
557 25 : CHECK_EQ(1, env->Global()
558 : ->Get(env.local(), v8_str("count"))
559 : .ToLocalChecked()
560 : ->Int32Value(env.local())
561 : .FromJust());
562 25 : CHECK_EQ(-1, env->Global()
563 : ->Get(env.local(), v8_str("result"))
564 : .ToLocalChecked()
565 : ->Int32Value(env.local())
566 : .FromJust());
567 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
568 : }
569 5 : isolate->Exit();
570 5 : isolate->Dispose();
571 5 : }
572 :
573 :
574 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationMUL) {
575 : ManualGCScope manual_gc_scope;
576 5 : i::FLAG_concurrent_recompilation = false;
577 : v8::Isolate::CreateParams create_params;
578 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
579 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
580 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
581 5 : isolate->Enter();
582 : {
583 5 : LocalContext env(isolate);
584 10 : v8::HandleScope scope(env->GetIsolate());
585 :
586 5 : TestDeoptimizeBinaryOpHelper(&env, "*");
587 :
588 25 : CHECK_EQ(1, env->Global()
589 : ->Get(env.local(), v8_str("count"))
590 : .ToLocalChecked()
591 : ->Int32Value(env.local())
592 : .FromJust());
593 25 : CHECK_EQ(56, env->Global()
594 : ->Get(env.local(), v8_str("result"))
595 : .ToLocalChecked()
596 : ->Int32Value(env.local())
597 : .FromJust());
598 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
599 : }
600 5 : isolate->Exit();
601 5 : isolate->Dispose();
602 5 : }
603 :
604 :
605 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationDIV) {
606 : ManualGCScope manual_gc_scope;
607 5 : i::FLAG_concurrent_recompilation = false;
608 : v8::Isolate::CreateParams create_params;
609 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
610 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
611 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
612 5 : isolate->Enter();
613 : {
614 5 : LocalContext env(isolate);
615 10 : v8::HandleScope scope(env->GetIsolate());
616 :
617 5 : TestDeoptimizeBinaryOpHelper(&env, "/");
618 :
619 25 : CHECK_EQ(1, env->Global()
620 : ->Get(env.local(), v8_str("count"))
621 : .ToLocalChecked()
622 : ->Int32Value(env.local())
623 : .FromJust());
624 25 : CHECK_EQ(0, env->Global()
625 : ->Get(env.local(), v8_str("result"))
626 : .ToLocalChecked()
627 : ->Int32Value(env.local())
628 : .FromJust());
629 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
630 : }
631 5 : isolate->Exit();
632 5 : isolate->Dispose();
633 5 : }
634 :
635 :
636 26644 : UNINITIALIZED_TEST(DeoptimizeBinaryOperationMOD) {
637 : ManualGCScope manual_gc_scope;
638 5 : i::FLAG_concurrent_recompilation = false;
639 : v8::Isolate::CreateParams create_params;
640 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
641 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
642 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
643 5 : isolate->Enter();
644 : {
645 5 : LocalContext env(isolate);
646 10 : v8::HandleScope scope(env->GetIsolate());
647 :
648 5 : TestDeoptimizeBinaryOpHelper(&env, "%");
649 :
650 25 : CHECK_EQ(1, env->Global()
651 : ->Get(env.local(), v8_str("count"))
652 : .ToLocalChecked()
653 : ->Int32Value(env.local())
654 : .FromJust());
655 25 : CHECK_EQ(7, env->Global()
656 : ->Get(env.local(), v8_str("result"))
657 : .ToLocalChecked()
658 : ->Int32Value(env.local())
659 : .FromJust());
660 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
661 : }
662 5 : isolate->Exit();
663 5 : isolate->Dispose();
664 5 : }
665 :
666 :
667 26644 : UNINITIALIZED_TEST(DeoptimizeCompare) {
668 : ManualGCScope manual_gc_scope;
669 5 : i::FLAG_concurrent_recompilation = false;
670 : v8::Isolate::CreateParams create_params;
671 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
672 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
673 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
674 5 : isolate->Enter();
675 : {
676 5 : LocalContext env(isolate);
677 10 : v8::HandleScope scope(env->GetIsolate());
678 :
679 : const char* f_source = "function f(x, y) { return x < y; };";
680 :
681 : {
682 : AllowNativesSyntaxNoInlining options;
683 : // Compile function f and collect to type feedback to insert compare ic
684 : // call in the optimized code.
685 5 : i::FLAG_prepare_always_opt = true;
686 : CompileRun(
687 : "var count = 0;"
688 : "var result = 0;"
689 : "var deopt = false;"
690 : "function X() { };"
691 : "X.prototype.toString = function () {"
692 : " if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'"
693 : "};");
694 : CompileRun(f_source);
695 : CompileRun(
696 : "for (var i = 0; i < 5; i++) {"
697 : " f('a', new X());"
698 : "};");
699 :
700 : // Compile an optimized version of f.
701 5 : i::FLAG_always_opt = true;
702 : CompileRun(f_source);
703 : CompileRun("f('a', new X());");
704 13 : CHECK(!i_isolate->use_optimizer() ||
705 : GetJSFunction(env.local(), "f")->IsOptimized());
706 :
707 : // Call f and force deoptimization while processing the comparison.
708 : CompileRun(
709 : "deopt = true;"
710 : "var result = f('a', new X());");
711 : }
712 5 : CcTest::CollectAllGarbage(i_isolate);
713 :
714 10 : CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
715 25 : CHECK_EQ(1, env->Global()
716 : ->Get(env.local(), v8_str("count"))
717 : .ToLocalChecked()
718 : ->Int32Value(env.local())
719 : .FromJust());
720 20 : CHECK_EQ(true, env->Global()
721 : ->Get(env.local(), v8_str("result"))
722 : .ToLocalChecked()
723 : ->BooleanValue(isolate));
724 5 : CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
725 : }
726 5 : isolate->Exit();
727 5 : isolate->Dispose();
728 5 : }
729 :
730 :
731 26644 : UNINITIALIZED_TEST(DeoptimizeLoadICStoreIC) {
732 : ManualGCScope manual_gc_scope;
733 5 : i::FLAG_concurrent_recompilation = false;
734 : v8::Isolate::CreateParams create_params;
735 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
736 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
737 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
738 5 : isolate->Enter();
739 : {
740 5 : LocalContext env(isolate);
741 10 : v8::HandleScope scope(env->GetIsolate());
742 :
743 : // Functions to generate load/store/keyed load/keyed store IC calls.
744 : const char* f1_source = "function f1(x) { return x.y; };";
745 : const char* g1_source = "function g1(x) { x.y = 1; };";
746 : const char* f2_source = "function f2(x, y) { return x[y]; };";
747 : const char* g2_source = "function g2(x, y) { x[y] = 1; };";
748 :
749 : {
750 : AllowNativesSyntaxNoInlining options;
751 : // Compile functions and collect to type feedback to insert ic
752 : // calls in the optimized code.
753 5 : i::FLAG_prepare_always_opt = true;
754 : CompileRun(
755 : "var count = 0;"
756 : "var result = 0;"
757 : "var deopt = false;"
758 : "function X() { };"
759 : "X.prototype.__defineGetter__('y', function () {"
760 : " if (deopt) { count++; %DeoptimizeFunction(f1); };"
761 : " return 13;"
762 : "});"
763 : "X.prototype.__defineSetter__('y', function () {"
764 : " if (deopt) { count++; %DeoptimizeFunction(g1); };"
765 : "});"
766 : "X.prototype.__defineGetter__('z', function () {"
767 : " if (deopt) { count++; %DeoptimizeFunction(f2); };"
768 : " return 13;"
769 : "});"
770 : "X.prototype.__defineSetter__('z', function () {"
771 : " if (deopt) { count++; %DeoptimizeFunction(g2); };"
772 : "});");
773 : CompileRun(f1_source);
774 : CompileRun(g1_source);
775 : CompileRun(f2_source);
776 : CompileRun(g2_source);
777 : CompileRun(
778 : "for (var i = 0; i < 5; i++) {"
779 : " f1(new X());"
780 : " g1(new X());"
781 : " f2(new X(), 'z');"
782 : " g2(new X(), 'z');"
783 : "};");
784 :
785 : // Compile an optimized version of the functions.
786 5 : i::FLAG_always_opt = true;
787 : CompileRun(f1_source);
788 : CompileRun(g1_source);
789 : CompileRun(f2_source);
790 : CompileRun(g2_source);
791 : CompileRun("f1(new X());");
792 : CompileRun("g1(new X());");
793 : CompileRun("f2(new X(), 'z');");
794 : CompileRun("g2(new X(), 'z');");
795 5 : if (i_isolate->use_optimizer()) {
796 8 : CHECK(GetJSFunction(env.local(), "f1")->IsOptimized());
797 8 : CHECK(GetJSFunction(env.local(), "g1")->IsOptimized());
798 8 : CHECK(GetJSFunction(env.local(), "f2")->IsOptimized());
799 8 : CHECK(GetJSFunction(env.local(), "g2")->IsOptimized());
800 : }
801 :
802 : // Call functions and force deoptimization while processing the ics.
803 : CompileRun(
804 : "deopt = true;"
805 : "var result = f1(new X());"
806 : "g1(new X());"
807 : "f2(new X(), 'z');"
808 : "g2(new X(), 'z');");
809 : }
810 5 : CcTest::CollectAllGarbage(i_isolate);
811 :
812 10 : CHECK(!GetJSFunction(env.local(), "f1")->IsOptimized());
813 10 : CHECK(!GetJSFunction(env.local(), "g1")->IsOptimized());
814 10 : CHECK(!GetJSFunction(env.local(), "f2")->IsOptimized());
815 10 : CHECK(!GetJSFunction(env.local(), "g2")->IsOptimized());
816 25 : CHECK_EQ(4, env->Global()
817 : ->Get(env.local(), v8_str("count"))
818 : .ToLocalChecked()
819 : ->Int32Value(env.local())
820 : .FromJust());
821 25 : CHECK_EQ(13, env->Global()
822 : ->Get(env.local(), v8_str("result"))
823 : .ToLocalChecked()
824 : ->Int32Value(env.local())
825 : .FromJust());
826 : }
827 5 : isolate->Exit();
828 5 : isolate->Dispose();
829 5 : }
830 :
831 :
832 26644 : UNINITIALIZED_TEST(DeoptimizeLoadICStoreICNested) {
833 : ManualGCScope manual_gc_scope;
834 5 : i::FLAG_concurrent_recompilation = false;
835 : v8::Isolate::CreateParams create_params;
836 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
837 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
838 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
839 5 : isolate->Enter();
840 : {
841 5 : LocalContext env(isolate);
842 10 : v8::HandleScope scope(env->GetIsolate());
843 :
844 : // Functions to generate load/store/keyed load/keyed store IC calls.
845 : const char* f1_source = "function f1(x) { return x.y; };";
846 : const char* g1_source = "function g1(x) { x.y = 1; };";
847 : const char* f2_source = "function f2(x, y) { return x[y]; };";
848 : const char* g2_source = "function g2(x, y) { x[y] = 1; };";
849 :
850 : {
851 : AllowNativesSyntaxNoInlining options;
852 : // Compile functions and collect to type feedback to insert ic
853 : // calls in the optimized code.
854 5 : i::FLAG_prepare_always_opt = true;
855 : CompileRun(
856 : "var count = 0;"
857 : "var result = 0;"
858 : "var deopt = false;"
859 : "function X() { };"
860 : "X.prototype.__defineGetter__('y', function () {"
861 : " g1(this);"
862 : " return 13;"
863 : "});"
864 : "X.prototype.__defineSetter__('y', function () {"
865 : " f2(this, 'z');"
866 : "});"
867 : "X.prototype.__defineGetter__('z', function () {"
868 : " g2(this, 'z');"
869 : "});"
870 : "X.prototype.__defineSetter__('z', function () {"
871 : " if (deopt) {"
872 : " count++;"
873 : " %DeoptimizeFunction(f1);"
874 : " %DeoptimizeFunction(g1);"
875 : " %DeoptimizeFunction(f2);"
876 : " %DeoptimizeFunction(g2); };"
877 : "});");
878 : CompileRun(f1_source);
879 : CompileRun(g1_source);
880 : CompileRun(f2_source);
881 : CompileRun(g2_source);
882 : CompileRun(
883 : "for (var i = 0; i < 5; i++) {"
884 : " f1(new X());"
885 : " g1(new X());"
886 : " f2(new X(), 'z');"
887 : " g2(new X(), 'z');"
888 : "};");
889 :
890 : // Compile an optimized version of the functions.
891 5 : i::FLAG_always_opt = true;
892 : CompileRun(f1_source);
893 : CompileRun(g1_source);
894 : CompileRun(f2_source);
895 : CompileRun(g2_source);
896 : CompileRun("f1(new X());");
897 : CompileRun("g1(new X());");
898 : CompileRun("f2(new X(), 'z');");
899 : CompileRun("g2(new X(), 'z');");
900 5 : if (i_isolate->use_optimizer()) {
901 8 : CHECK(GetJSFunction(env.local(), "f1")->IsOptimized());
902 8 : CHECK(GetJSFunction(env.local(), "g1")->IsOptimized());
903 8 : CHECK(GetJSFunction(env.local(), "f2")->IsOptimized());
904 8 : CHECK(GetJSFunction(env.local(), "g2")->IsOptimized());
905 : }
906 :
907 : // Call functions and force deoptimization while processing the ics.
908 : CompileRun(
909 : "deopt = true;"
910 : "var result = f1(new X());");
911 : }
912 5 : CcTest::CollectAllGarbage(i_isolate);
913 :
914 10 : CHECK(!GetJSFunction(env.local(), "f1")->IsOptimized());
915 10 : CHECK(!GetJSFunction(env.local(), "g1")->IsOptimized());
916 10 : CHECK(!GetJSFunction(env.local(), "f2")->IsOptimized());
917 10 : CHECK(!GetJSFunction(env.local(), "g2")->IsOptimized());
918 25 : CHECK_EQ(1, env->Global()
919 : ->Get(env.local(), v8_str("count"))
920 : .ToLocalChecked()
921 : ->Int32Value(env.local())
922 : .FromJust());
923 25 : CHECK_EQ(13, env->Global()
924 : ->Get(env.local(), v8_str("result"))
925 : .ToLocalChecked()
926 : ->Int32Value(env.local())
927 : .FromJust());
928 : }
929 5 : isolate->Exit();
930 5 : isolate->Dispose();
931 79922 : }
|