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