Home | History | Annotate | Download | only in cctest
      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:
     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
     85 class 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.
    111 static void NonIncrementalGC(i::Isolate* isolate) {
    112   isolate->heap()->CollectAllGarbage();
    113 }
    114 
    115 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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