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