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
40using ::v8::base::OS;
41using ::v8::internal::Deoptimizer;
42using ::v8::internal::EmbeddedVector;
43using ::v8::internal::Handle;
44using ::v8::internal::Isolate;
45using ::v8::internal::JSFunction;
46using ::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
54class AlwaysOptimizeAllowNativesSyntaxNoInlining {
55 public:
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
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
85class AllowNativesSyntaxNoInlining {
86 public:
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
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.
111static void NonIncrementalGC(i::Isolate* isolate) {
112  isolate->heap()->CollectAllGarbage();
113}
114
115
116static 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
124TEST(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
170TEST(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
217TEST(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
250TEST(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
287TEST(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
323TEST(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
376TEST(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
413UNINITIALIZED_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
477static 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
488static 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
522UNINITIALIZED_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
552UNINITIALIZED_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
582UNINITIALIZED_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
612UNINITIALIZED_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
642UNINITIALIZED_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
672UNINITIALIZED_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
736UNINITIALIZED_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
836UNINITIALIZED_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