test-deoptimization.cc 31.7 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <stdlib.h>

30
#include "src/v8.h"
31

32
#include "src/api-inl.h"
33
#include "src/base/platform/platform.h"
34
#include "src/compilation-cache.h"
35
#include "src/debug/debug.h"
36 37
#include "src/deoptimizer.h"
#include "src/isolate.h"
38
#include "src/objects-inl.h"
39
#include "test/cctest/cctest.h"
40

41
using ::v8::base::OS;
42 43
using ::v8::internal::Deoptimizer;
using ::v8::internal::EmbeddedVector;
44 45
using ::v8::internal::Handle;
using ::v8::internal::JSFunction;
46 47 48 49

// Size of temp buffer for formatting small strings.
#define SMALL_STRING_BUFFER_SIZE 80

50 51
// Utility class to set the following runtime flags when constructed and return
// to their default state when destroyed:
52
//   --allow-natives-syntax --always-opt --noturbo-inlining
53 54 55 56 57
class AlwaysOptimizeAllowNativesSyntaxNoInlining {
 public:
  AlwaysOptimizeAllowNativesSyntaxNoInlining()
      : always_opt_(i::FLAG_always_opt),
        allow_natives_syntax_(i::FLAG_allow_natives_syntax),
58
        turbo_inlining_(i::FLAG_turbo_inlining) {
59 60
    i::FLAG_always_opt = true;
    i::FLAG_allow_natives_syntax = true;
61
    i::FLAG_turbo_inlining = false;
62 63 64 65
  }

  ~AlwaysOptimizeAllowNativesSyntaxNoInlining() {
    i::FLAG_always_opt = always_opt_;
66 67
    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
    i::FLAG_turbo_inlining = turbo_inlining_;
68 69 70 71 72
  }

 private:
  bool always_opt_;
  bool allow_natives_syntax_;
73
  bool turbo_inlining_;
74 75
};

76 77
// Utility class to set the following runtime flags when constructed and return
// to their default state when destroyed:
78
//   --allow-natives-syntax --noturbo-inlining
79
class AllowNativesSyntaxNoInlining {
80
 public:
81
  AllowNativesSyntaxNoInlining()
82
      : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
83
        turbo_inlining_(i::FLAG_turbo_inlining) {
84
    i::FLAG_allow_natives_syntax = true;
85
    i::FLAG_turbo_inlining = false;
86 87
  }

88
  ~AllowNativesSyntaxNoInlining() {
89
    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
90
    i::FLAG_turbo_inlining = turbo_inlining_;
91 92 93 94
  }

 private:
  bool allow_natives_syntax_;
95
  bool turbo_inlining_;
96 97
};

98
static Handle<JSFunction> GetJSFunction(v8::Local<v8::Context> context,
99
                                        const char* property_name) {
100 101
  v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
      context->Global()->Get(context, v8_str(property_name)).ToLocalChecked());
102
  return i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*fun));
103 104 105 106
}


TEST(DeoptimizeSimple) {
107
  ManualGCScope manual_gc_scope;
108
  LocalContext env;
109
  v8::HandleScope scope(env->GetIsolate());
110 111 112 113 114 115 116 117 118

  // Test lazy deoptimization of a simple function.
  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "function h() { %DeoptimizeFunction(f); }"
        "function g() { count++; h(); }"
        "function f() { g(); };"
119
        "f();");
120
  }
121
  CcTest::CollectAllGarbage();
122

123 124 125 126 127 128
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
129
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
130 131 132 133 134 135 136 137 138

  // Test lazy deoptimization of a simple function. Call the function after the
  // deoptimization while it is still activated further down the stack.
  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "function g() { count++; %DeoptimizeFunction(f); f(false); }"
        "function f(x) { if (x) { g(); } else { return } };"
139
        "f(true);");
140
  }
141
  CcTest::CollectAllGarbage();
142

143 144 145 146 147 148
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
149
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
150 151 152 153
}


TEST(DeoptimizeSimpleWithArguments) {
154
  ManualGCScope manual_gc_scope;
155
  LocalContext env;
156
  v8::HandleScope scope(env->GetIsolate());
157 158 159 160 161 162 163 164 165

  // Test lazy deoptimization of a simple function with some arguments.
  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "function h(x) { %DeoptimizeFunction(f); }"
        "function g(x, y) { count++; h(x); }"
        "function f(x, y, z) { g(1,x); y+z; };"
166
        "f(1, \"2\", false);");
167
  }
168
  CcTest::CollectAllGarbage();
169

170 171 172 173 174 175
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
176
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
177 178 179 180 181 182 183 184 185 186

  // Test lazy deoptimization of a simple function with some arguments. Call the
  // function after the deoptimization while it is still activated further down
  // the stack.
  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "function g(x, y) { count++; %DeoptimizeFunction(f); f(false, 1, y); }"
        "function f(x, y, z) { if (x) { g(x, y); } else { return y + z; } };"
187
        "f(true, 1, \"2\");");
188
  }
189
  CcTest::CollectAllGarbage();
190

191 192 193 194 195 196
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
197
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
198 199 200 201
}


TEST(DeoptimizeSimpleNested) {
202
  ManualGCScope manual_gc_scope;
203
  LocalContext env;
204
  v8::HandleScope scope(env->GetIsolate());
205 206 207 208 209 210 211 212 213 214 215

  // Test lazy deoptimization of a simple function. Have a nested function call
  // do the deoptimization.
  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "var result = 0;"
        "function h(x, y, z) { return x + y + z; }"
        "function g(z) { count++; %DeoptimizeFunction(f); return z;}"
        "function f(x,y,z) { return h(x, y, g(z)); };"
216
        "result = f(1, 2, 3);");
217
    CcTest::CollectAllGarbage();
218

219 220 221 222 223 224 225 226 227 228 229
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(6, env->Global()
                    ->Get(env.local(), v8_str("result"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
230
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
231 232 233 234 235
  }
}


TEST(DeoptimizeRecursive) {
236
  ManualGCScope manual_gc_scope;
237
  LocalContext env;
238
  v8::HandleScope scope(env->GetIsolate());
239 240 241 242 243 244 245 246 247 248

  {
    // Test lazy deoptimization of a simple function called recursively. Call
    // the function recursively a number of times before deoptimizing it.
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "var calls = 0;"
        "function g() { count++; %DeoptimizeFunction(f); }"
        "function f(x) { calls++; if (x > 0) { f(x - 1); } else { g(); } };"
249
        "f(10);");
250
  }
251
  CcTest::CollectAllGarbage();
252

253 254 255 256 257 258 259 260 261 262
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK_EQ(11, env->Global()
                   ->Get(env.local(), v8_str("calls"))
                   .ToLocalChecked()
                   ->Int32Value(env.local())
                   .FromJust());
263
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
264

265
  v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
266 267 268
      env->Global()
          ->Get(env.local(), v8_str(CcTest::isolate(), "f"))
          .ToLocalChecked());
269
  CHECK(!fun.IsEmpty());
270 271 272 273
}


TEST(DeoptimizeMultiple) {
274
  ManualGCScope manual_gc_scope;
275
  LocalContext env;
276
  v8::HandleScope scope(env->GetIsolate());
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "var result = 0;"
        "function g() { count++;"
        "               %DeoptimizeFunction(f1);"
        "               %DeoptimizeFunction(f2);"
        "               %DeoptimizeFunction(f3);"
        "               %DeoptimizeFunction(f4);}"
        "function f4(x) { g(); };"
        "function f3(x, y, z) { f4(); return x + y + z; };"
        "function f2(x, y) { return x + f3(y + 1, y + 1, y + 1) + y; };"
        "function f1(x) { return f2(x + 1, x + 1) + x; };"
292
        "result = f1(1);");
293
  }
294
  CcTest::CollectAllGarbage();
295

296 297 298 299 300 301 302 303 304 305
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK_EQ(14, env->Global()
                   ->Get(env.local(), v8_str("result"))
                   .ToLocalChecked()
                   ->Int32Value(env.local())
                   .FromJust());
306
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
307 308 309 310
}


TEST(DeoptimizeConstructor) {
311
  ManualGCScope manual_gc_scope;
312
  LocalContext env;
313
  v8::HandleScope scope(env->GetIsolate());
314 315 316 317 318 319 320 321

  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "function g() { count++;"
        "               %DeoptimizeFunction(f); }"
        "function f() {  g(); };"
322
        "result = new f() instanceof f;");
323
  }
324
  CcTest::CollectAllGarbage();
325

326 327 328 329 330 331 332 333 334
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK(env->Global()
            ->Get(env.local(), v8_str("result"))
            .ToLocalChecked()
            ->IsTrue());
335
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
336 337 338 339 340 341 342 343 344 345

  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "var result = 0;"
        "function g() { count++;"
        "               %DeoptimizeFunction(f); }"
        "function f(x, y) { this.x = x; g(); this.y = y; };"
        "result = new f(1, 2);"
346
        "result = result.x + result.y;");
347
  }
348
  CcTest::CollectAllGarbage();
349

350 351 352 353 354 355 356 357 358 359
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK_EQ(3, env->Global()
                  ->Get(env.local(), v8_str("result"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
360
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
361 362 363 364
}


TEST(DeoptimizeConstructorMultiple) {
365
  ManualGCScope manual_gc_scope;
366
  LocalContext env;
367
  v8::HandleScope scope(env->GetIsolate());
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

  {
    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
    CompileRun(
        "var count = 0;"
        "var result = 0;"
        "function g() { count++;"
        "               %DeoptimizeFunction(f1);"
        "               %DeoptimizeFunction(f2);"
        "               %DeoptimizeFunction(f3);"
        "               %DeoptimizeFunction(f4);}"
        "function f4(x) { this.result = x; g(); };"
        "function f3(x, y, z) { this.result = new f4(x + y + z).result; };"
        "function f2(x, y) {"
        "    this.result = x + new f3(y + 1, y + 1, y + 1).result + y; };"
        "function f1(x) { this.result = new f2(x + 1, x + 1).result + x; };"
384
        "result = new f1(1).result;");
385
  }
386
  CcTest::CollectAllGarbage();
387

388 389 390 391 392 393 394 395 396 397
  CHECK_EQ(1, env->Global()
                  ->Get(env.local(), v8_str("count"))
                  .ToLocalChecked()
                  ->Int32Value(env.local())
                  .FromJust());
  CHECK_EQ(14, env->Global()
                   ->Get(env.local(), v8_str("result"))
                   .ToLocalChecked()
                   ->Int32Value(env.local())
                   .FromJust());
398
  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
399 400 401
}


402
UNINITIALIZED_TEST(DeoptimizeBinaryOperationADDString) {
403
  ManualGCScope manual_gc_scope;
404 405
  i::FLAG_concurrent_recompilation = false;
  AllowNativesSyntaxNoInlining options;
406 407 408
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
409 410
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
411
  {
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());

    const char* f_source = "function f(x, y) { return x + y; };";

    {
      // Compile function f and collect to type feedback to insert binary op
      // stub call in the optimized code.
      i::FLAG_prepare_always_opt = true;
      CompileRun(
          "var count = 0;"
          "var result = 0;"
          "var deopt = false;"
          "function X() { };"
          "X.prototype.toString = function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'"
          "};");
      CompileRun(f_source);
      CompileRun(
          "for (var i = 0; i < 5; i++) {"
          "  f('a+', new X());"
          "};");

      // Compile an optimized version of f.
      i::FLAG_always_opt = true;
      CompileRun(f_source);
      CompileRun("f('a+', new X());");
Mythri's avatar
Mythri committed
439
      CHECK(!i_isolate->use_optimizer() ||
440
            GetJSFunction(env.local(), "f")->IsOptimized());
441 442 443 444 445 446

      // Call f and force deoptimization while processing the binary operation.
      CompileRun(
          "deopt = true;"
          "var result = f('a+', new X());");
    }
447
    CcTest::CollectAllGarbage(i_isolate);
448

449 450 451 452 453 454 455 456
    CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    v8::Local<v8::Value> result =
        env->Global()->Get(env.local(), v8_str("result")).ToLocalChecked();
457
    CHECK(result->IsString());
458
    v8::String::Utf8Value utf8(isolate, result);
459
    CHECK_EQ(0, strcmp("a+an X", *utf8));
460 461 462 463
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
}


static void CompileConstructorWithDeoptimizingValueOf() {
  CompileRun("var count = 0;"
             "var result = 0;"
             "var deopt = false;"
             "function X() { };"
             "X.prototype.valueOf = function () {"
             "  if (deopt) { count++; %DeoptimizeFunction(f); } return 8"
             "};");
}


static void TestDeoptimizeBinaryOpHelper(LocalContext* env,
                                         const char* binary_op) {
480
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>((*env)->GetIsolate());
481
  EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer;
482 483 484
  SNPrintF(f_source_buffer,
           "function f(x, y) { return x %s y; };",
           binary_op);
485 486
  char* f_source = f_source_buffer.start();

487
  AllowNativesSyntaxNoInlining options;
488 489 490 491 492 493 494 495 496 497 498 499 500
  // Compile function f and collect to type feedback to insert binary op stub
  // call in the optimized code.
  i::FLAG_prepare_always_opt = true;
  CompileConstructorWithDeoptimizingValueOf();
  CompileRun(f_source);
  CompileRun("for (var i = 0; i < 5; i++) {"
             "  f(8, new X());"
             "};");

  // Compile an optimized version of f.
  i::FLAG_always_opt = true;
  CompileRun(f_source);
  CompileRun("f(7, new X());");
Mythri's avatar
Mythri committed
501
  CHECK(!i_isolate->use_optimizer() ||
502
        GetJSFunction((*env).local(), "f")->IsOptimized());
503 504 505

  // Call f and force deoptimization while processing the binary operation.
  CompileRun("deopt = true;"
506
             "var result = f(7, new X());");
507
  CcTest::CollectAllGarbage(i_isolate);
508
  CHECK(!GetJSFunction((*env).local(), "f")->IsOptimized());
509 510 511
}


512
UNINITIALIZED_TEST(DeoptimizeBinaryOperationADD) {
513
  ManualGCScope manual_gc_scope;
514
  i::FLAG_concurrent_recompilation = false;
515 516 517
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
518 519 520 521 522
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
  {
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());
523

524
    TestDeoptimizeBinaryOpHelper(&env, "+");
525

526 527 528 529 530 531 532 533 534 535
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(15, env->Global()
                     ->Get(env.local(), v8_str("result"))
                     .ToLocalChecked()
                     ->Int32Value(env.local())
                     .FromJust());
536 537 538 539
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
540 541 542
}


543
UNINITIALIZED_TEST(DeoptimizeBinaryOperationSUB) {
544
  ManualGCScope manual_gc_scope;
545
  i::FLAG_concurrent_recompilation = false;
546 547 548
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
549 550 551 552 553
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
  {
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());
554

555
    TestDeoptimizeBinaryOpHelper(&env, "-");
556

557 558 559 560 561 562 563 564 565 566
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(-1, env->Global()
                     ->Get(env.local(), v8_str("result"))
                     .ToLocalChecked()
                     ->Int32Value(env.local())
                     .FromJust());
567 568 569 570
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
571 572 573
}


574
UNINITIALIZED_TEST(DeoptimizeBinaryOperationMUL) {
575
  ManualGCScope manual_gc_scope;
576
  i::FLAG_concurrent_recompilation = false;
577 578 579
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
580 581 582 583 584
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
  {
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());
585

586
    TestDeoptimizeBinaryOpHelper(&env, "*");
587

588 589 590 591 592 593 594 595 596 597
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(56, env->Global()
                     ->Get(env.local(), v8_str("result"))
                     .ToLocalChecked()
                     ->Int32Value(env.local())
                     .FromJust());
598 599 600 601
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
602 603 604
}


605
UNINITIALIZED_TEST(DeoptimizeBinaryOperationDIV) {
606
  ManualGCScope manual_gc_scope;
607
  i::FLAG_concurrent_recompilation = false;
608 609 610
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
611 612 613 614 615
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
  {
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());
616

617
    TestDeoptimizeBinaryOpHelper(&env, "/");
618

619 620 621 622 623 624 625 626 627 628
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(0, env->Global()
                    ->Get(env.local(), v8_str("result"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
629 630 631 632
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
633 634 635
}


636
UNINITIALIZED_TEST(DeoptimizeBinaryOperationMOD) {
637
  ManualGCScope manual_gc_scope;
638
  i::FLAG_concurrent_recompilation = false;
639 640 641
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
642 643 644 645 646
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
  {
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());
647

648
    TestDeoptimizeBinaryOpHelper(&env, "%");
649

650 651 652 653 654 655 656 657 658 659
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(7, env->Global()
                    ->Get(env.local(), v8_str("result"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
660 661 662 663
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
664 665 666
}


667
UNINITIALIZED_TEST(DeoptimizeCompare) {
668
  ManualGCScope manual_gc_scope;
669
  i::FLAG_concurrent_recompilation = false;
670 671 672
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
673 674
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
675
  {
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());

    const char* f_source = "function f(x, y) { return x < y; };";

    {
      AllowNativesSyntaxNoInlining options;
      // Compile function f and collect to type feedback to insert compare ic
      // call in the optimized code.
      i::FLAG_prepare_always_opt = true;
      CompileRun(
          "var count = 0;"
          "var result = 0;"
          "var deopt = false;"
          "function X() { };"
          "X.prototype.toString = function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'"
          "};");
      CompileRun(f_source);
      CompileRun(
          "for (var i = 0; i < 5; i++) {"
          "  f('a', new X());"
          "};");

      // Compile an optimized version of f.
      i::FLAG_always_opt = true;
      CompileRun(f_source);
      CompileRun("f('a', new X());");
Mythri's avatar
Mythri committed
704
      CHECK(!i_isolate->use_optimizer() ||
705
            GetJSFunction(env.local(), "f")->IsOptimized());
706 707 708 709 710 711

      // Call f and force deoptimization while processing the comparison.
      CompileRun(
          "deopt = true;"
          "var result = f('a', new X());");
    }
712
    CcTest::CollectAllGarbage(i_isolate);
713

714 715 716 717 718 719 720 721 722
    CHECK(!GetJSFunction(env.local(), "f")->IsOptimized());
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(true, env->Global()
                       ->Get(env.local(), v8_str("result"))
                       .ToLocalChecked()
723
                       ->BooleanValue(isolate));
724 725 726 727
    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
  }
  isolate->Exit();
  isolate->Dispose();
728 729 730
}


731
UNINITIALIZED_TEST(DeoptimizeLoadICStoreIC) {
732
  ManualGCScope manual_gc_scope;
733
  i::FLAG_concurrent_recompilation = false;
734 735 736
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
737 738
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
739
  {
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());

    // Functions to generate load/store/keyed load/keyed store IC calls.
    const char* f1_source = "function f1(x) { return x.y; };";
    const char* g1_source = "function g1(x) { x.y = 1; };";
    const char* f2_source = "function f2(x, y) { return x[y]; };";
    const char* g2_source = "function g2(x, y) { x[y] = 1; };";

    {
      AllowNativesSyntaxNoInlining options;
      // Compile functions and collect to type feedback to insert ic
      // calls in the optimized code.
      i::FLAG_prepare_always_opt = true;
      CompileRun(
          "var count = 0;"
          "var result = 0;"
          "var deopt = false;"
          "function X() { };"
          "X.prototype.__defineGetter__('y', function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(f1); };"
          "  return 13;"
          "});"
          "X.prototype.__defineSetter__('y', function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(g1); };"
          "});"
          "X.prototype.__defineGetter__('z', function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(f2); };"
          "  return 13;"
          "});"
          "X.prototype.__defineSetter__('z', function () {"
          "  if (deopt) { count++; %DeoptimizeFunction(g2); };"
          "});");
      CompileRun(f1_source);
      CompileRun(g1_source);
      CompileRun(f2_source);
      CompileRun(g2_source);
      CompileRun(
          "for (var i = 0; i < 5; i++) {"
          "  f1(new X());"
          "  g1(new X());"
          "  f2(new X(), 'z');"
          "  g2(new X(), 'z');"
          "};");

      // Compile an optimized version of the functions.
      i::FLAG_always_opt = true;
      CompileRun(f1_source);
      CompileRun(g1_source);
      CompileRun(f2_source);
      CompileRun(g2_source);
      CompileRun("f1(new X());");
      CompileRun("g1(new X());");
      CompileRun("f2(new X(), 'z');");
      CompileRun("g2(new X(), 'z');");
Mythri's avatar
Mythri committed
795
      if (i_isolate->use_optimizer()) {
796 797 798 799
        CHECK(GetJSFunction(env.local(), "f1")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "g1")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "f2")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "g2")->IsOptimized());
800 801 802 803 804 805 806 807 808
      }

      // Call functions and force deoptimization while processing the ics.
      CompileRun(
          "deopt = true;"
          "var result = f1(new X());"
          "g1(new X());"
          "f2(new X(), 'z');"
          "g2(new X(), 'z');");
809
    }
810
    CcTest::CollectAllGarbage(i_isolate);
811

812 813 814 815 816 817 818 819 820 821 822 823 824 825
    CHECK(!GetJSFunction(env.local(), "f1")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "g1")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "f2")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "g2")->IsOptimized());
    CHECK_EQ(4, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(13, env->Global()
                     ->Get(env.local(), v8_str("result"))
                     .ToLocalChecked()
                     ->Int32Value(env.local())
                     .FromJust());
826
  }
827 828
  isolate->Exit();
  isolate->Dispose();
829 830 831
}


832
UNINITIALIZED_TEST(DeoptimizeLoadICStoreICNested) {
833
  ManualGCScope manual_gc_scope;
834
  i::FLAG_concurrent_recompilation = false;
835 836 837
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
838 839
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  isolate->Enter();
840
  {
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
    LocalContext env(isolate);
    v8::HandleScope scope(env->GetIsolate());

    // Functions to generate load/store/keyed load/keyed store IC calls.
    const char* f1_source = "function f1(x) { return x.y; };";
    const char* g1_source = "function g1(x) { x.y = 1; };";
    const char* f2_source = "function f2(x, y) { return x[y]; };";
    const char* g2_source = "function g2(x, y) { x[y] = 1; };";

    {
      AllowNativesSyntaxNoInlining options;
      // Compile functions and collect to type feedback to insert ic
      // calls in the optimized code.
      i::FLAG_prepare_always_opt = true;
      CompileRun(
          "var count = 0;"
          "var result = 0;"
          "var deopt = false;"
          "function X() { };"
          "X.prototype.__defineGetter__('y', function () {"
          "  g1(this);"
          "  return 13;"
          "});"
          "X.prototype.__defineSetter__('y', function () {"
          "  f2(this, 'z');"
          "});"
          "X.prototype.__defineGetter__('z', function () {"
          "  g2(this, 'z');"
          "});"
          "X.prototype.__defineSetter__('z', function () {"
          "  if (deopt) {"
          "    count++;"
          "    %DeoptimizeFunction(f1);"
          "    %DeoptimizeFunction(g1);"
          "    %DeoptimizeFunction(f2);"
          "    %DeoptimizeFunction(g2); };"
          "});");
      CompileRun(f1_source);
      CompileRun(g1_source);
      CompileRun(f2_source);
      CompileRun(g2_source);
      CompileRun(
          "for (var i = 0; i < 5; i++) {"
          "  f1(new X());"
          "  g1(new X());"
          "  f2(new X(), 'z');"
          "  g2(new X(), 'z');"
          "};");

      // Compile an optimized version of the functions.
      i::FLAG_always_opt = true;
      CompileRun(f1_source);
      CompileRun(g1_source);
      CompileRun(f2_source);
      CompileRun(g2_source);
      CompileRun("f1(new X());");
      CompileRun("g1(new X());");
      CompileRun("f2(new X(), 'z');");
      CompileRun("g2(new X(), 'z');");
Mythri's avatar
Mythri committed
900
      if (i_isolate->use_optimizer()) {
901 902 903 904
        CHECK(GetJSFunction(env.local(), "f1")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "g1")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "f2")->IsOptimized());
        CHECK(GetJSFunction(env.local(), "g2")->IsOptimized());
905 906 907 908 909 910
      }

      // Call functions and force deoptimization while processing the ics.
      CompileRun(
          "deopt = true;"
          "var result = f1(new X());");
911
    }
912
    CcTest::CollectAllGarbage(i_isolate);
913

914 915 916 917 918 919 920 921 922 923 924 925 926 927
    CHECK(!GetJSFunction(env.local(), "f1")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "g1")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "f2")->IsOptimized());
    CHECK(!GetJSFunction(env.local(), "g2")->IsOptimized());
    CHECK_EQ(1, env->Global()
                    ->Get(env.local(), v8_str("count"))
                    .ToLocalChecked()
                    ->Int32Value(env.local())
                    .FromJust());
    CHECK_EQ(13, env->Global()
                     ->Get(env.local(), v8_str("result"))
                     .ToLocalChecked()
                     ->Int32Value(env.local())
                     .FromJust());
928
  }
929 930
  isolate->Exit();
  isolate->Dispose();
931
}