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