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.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 --allow-natives-syntax --always-opt and --nouse-inlining 52 // when constructed and return to their default state when destroyed. 53 class AlwaysOptimizeAllowNativesSyntaxNoInlining { 54 public: 55 AlwaysOptimizeAllowNativesSyntaxNoInlining() 56 : always_opt_(i::FLAG_always_opt), 57 allow_natives_syntax_(i::FLAG_allow_natives_syntax), 58 use_inlining_(i::FLAG_use_inlining) { 59 i::FLAG_always_opt = true; 60 i::FLAG_allow_natives_syntax = true; 61 i::FLAG_use_inlining = false; 62 } 63 64 ~AlwaysOptimizeAllowNativesSyntaxNoInlining() { 65 i::FLAG_allow_natives_syntax = allow_natives_syntax_; 66 i::FLAG_always_opt = always_opt_; 67 i::FLAG_use_inlining = use_inlining_; 68 } 69 70 private: 71 bool always_opt_; 72 bool allow_natives_syntax_; 73 bool use_inlining_; 74 }; 75 76 77 // Utility class to set --allow-natives-syntax and --nouse-inlining when 78 // constructed and return to their default state when destroyed. 79 class AllowNativesSyntaxNoInlining { 80 public: 81 AllowNativesSyntaxNoInlining() 82 : allow_natives_syntax_(i::FLAG_allow_natives_syntax), 83 use_inlining_(i::FLAG_use_inlining) { 84 i::FLAG_allow_natives_syntax = true; 85 i::FLAG_use_inlining = false; 86 } 87 88 ~AllowNativesSyntaxNoInlining() { 89 i::FLAG_allow_natives_syntax = allow_natives_syntax_; 90 i::FLAG_use_inlining = use_inlining_; 91 } 92 93 private: 94 bool allow_natives_syntax_; 95 bool use_inlining_; 96 }; 97 98 99 // Abort any ongoing incremental marking to make sure that all weak global 100 // handle callbacks are processed. 101 static void NonIncrementalGC(i::Isolate* isolate) { 102 isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 103 } 104 105 106 static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj, 107 const char* property_name) { 108 v8::Local<v8::Function> fun = 109 v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name))); 110 return v8::Utils::OpenHandle(*fun); 111 } 112 113 114 TEST(DeoptimizeSimple) { 115 i::FLAG_turbo_deoptimization = true; 116 117 LocalContext env; 118 v8::HandleScope scope(env->GetIsolate()); 119 120 // Test lazy deoptimization of a simple function. 121 { 122 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 123 CompileRun( 124 "var count = 0;" 125 "function h() { %DeoptimizeFunction(f); }" 126 "function g() { count++; h(); }" 127 "function f() { g(); };" 128 "f();"); 129 } 130 NonIncrementalGC(CcTest::i_isolate()); 131 132 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 133 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 134 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 135 136 // Test lazy deoptimization of a simple function. Call the function after the 137 // deoptimization while it is still activated further down the stack. 138 { 139 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 140 CompileRun( 141 "var count = 0;" 142 "function g() { count++; %DeoptimizeFunction(f); f(false); }" 143 "function f(x) { if (x) { g(); } else { return } };" 144 "f(true);"); 145 } 146 NonIncrementalGC(CcTest::i_isolate()); 147 148 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 149 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 150 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 151 } 152 153 154 TEST(DeoptimizeSimpleWithArguments) { 155 i::FLAG_turbo_deoptimization = true; 156 157 LocalContext env; 158 v8::HandleScope scope(env->GetIsolate()); 159 160 // Test lazy deoptimization of a simple function with some arguments. 161 { 162 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 163 CompileRun( 164 "var count = 0;" 165 "function h(x) { %DeoptimizeFunction(f); }" 166 "function g(x, y) { count++; h(x); }" 167 "function f(x, y, z) { g(1,x); y+z; };" 168 "f(1, \"2\", false);"); 169 } 170 NonIncrementalGC(CcTest::i_isolate()); 171 172 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 173 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 174 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 175 176 // Test lazy deoptimization of a simple function with some arguments. Call the 177 // function after the deoptimization while it is still activated further down 178 // the stack. 179 { 180 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 181 CompileRun( 182 "var count = 0;" 183 "function g(x, y) { count++; %DeoptimizeFunction(f); f(false, 1, y); }" 184 "function f(x, y, z) { if (x) { g(x, y); } else { return y + z; } };" 185 "f(true, 1, \"2\");"); 186 } 187 NonIncrementalGC(CcTest::i_isolate()); 188 189 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 190 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 191 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 192 } 193 194 195 TEST(DeoptimizeSimpleNested) { 196 i::FLAG_turbo_deoptimization = true; 197 198 LocalContext env; 199 v8::HandleScope scope(env->GetIsolate()); 200 201 // Test lazy deoptimization of a simple function. Have a nested function call 202 // do the deoptimization. 203 { 204 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 205 CompileRun( 206 "var count = 0;" 207 "var result = 0;" 208 "function h(x, y, z) { return x + y + z; }" 209 "function g(z) { count++; %DeoptimizeFunction(f); return z;}" 210 "function f(x,y,z) { return h(x, y, g(z)); };" 211 "result = f(1, 2, 3);"); 212 NonIncrementalGC(CcTest::i_isolate()); 213 214 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 215 CHECK_EQ(6, env->Global()->Get(v8_str("result"))->Int32Value()); 216 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 217 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 218 } 219 } 220 221 222 TEST(DeoptimizeRecursive) { 223 i::FLAG_turbo_deoptimization = true; 224 LocalContext env; 225 v8::HandleScope scope(env->GetIsolate()); 226 227 { 228 // Test lazy deoptimization of a simple function called recursively. Call 229 // the function recursively a number of times before deoptimizing it. 230 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 231 CompileRun( 232 "var count = 0;" 233 "var calls = 0;" 234 "function g() { count++; %DeoptimizeFunction(f); }" 235 "function f(x) { calls++; if (x > 0) { f(x - 1); } else { g(); } };" 236 "f(10);"); 237 } 238 NonIncrementalGC(CcTest::i_isolate()); 239 240 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 241 CHECK_EQ(11, env->Global()->Get(v8_str("calls"))->Int32Value()); 242 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 243 244 v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast( 245 env->Global()->Get(v8::String::NewFromUtf8(CcTest::isolate(), "f"))); 246 CHECK(!fun.IsEmpty()); 247 } 248 249 250 TEST(DeoptimizeMultiple) { 251 i::FLAG_turbo_deoptimization = true; 252 LocalContext env; 253 v8::HandleScope scope(env->GetIsolate()); 254 255 { 256 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 257 CompileRun( 258 "var count = 0;" 259 "var result = 0;" 260 "function g() { count++;" 261 " %DeoptimizeFunction(f1);" 262 " %DeoptimizeFunction(f2);" 263 " %DeoptimizeFunction(f3);" 264 " %DeoptimizeFunction(f4);}" 265 "function f4(x) { g(); };" 266 "function f3(x, y, z) { f4(); return x + y + z; };" 267 "function f2(x, y) { return x + f3(y + 1, y + 1, y + 1) + y; };" 268 "function f1(x) { return f2(x + 1, x + 1) + x; };" 269 "result = f1(1);"); 270 } 271 NonIncrementalGC(CcTest::i_isolate()); 272 273 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 274 CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value()); 275 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 276 } 277 278 279 TEST(DeoptimizeConstructor) { 280 i::FLAG_turbo_deoptimization = true; 281 LocalContext env; 282 v8::HandleScope scope(env->GetIsolate()); 283 284 { 285 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 286 CompileRun( 287 "var count = 0;" 288 "function g() { count++;" 289 " %DeoptimizeFunction(f); }" 290 "function f() { g(); };" 291 "result = new f() instanceof f;"); 292 } 293 NonIncrementalGC(CcTest::i_isolate()); 294 295 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 296 CHECK(env->Global()->Get(v8_str("result"))->IsTrue()); 297 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 298 299 { 300 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 301 CompileRun( 302 "var count = 0;" 303 "var result = 0;" 304 "function g() { count++;" 305 " %DeoptimizeFunction(f); }" 306 "function f(x, y) { this.x = x; g(); this.y = y; };" 307 "result = new f(1, 2);" 308 "result = result.x + result.y;"); 309 } 310 NonIncrementalGC(CcTest::i_isolate()); 311 312 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 313 CHECK_EQ(3, env->Global()->Get(v8_str("result"))->Int32Value()); 314 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 315 } 316 317 318 TEST(DeoptimizeConstructorMultiple) { 319 i::FLAG_turbo_deoptimization = true; 320 LocalContext env; 321 v8::HandleScope scope(env->GetIsolate()); 322 323 { 324 AlwaysOptimizeAllowNativesSyntaxNoInlining options; 325 CompileRun( 326 "var count = 0;" 327 "var result = 0;" 328 "function g() { count++;" 329 " %DeoptimizeFunction(f1);" 330 " %DeoptimizeFunction(f2);" 331 " %DeoptimizeFunction(f3);" 332 " %DeoptimizeFunction(f4);}" 333 "function f4(x) { this.result = x; g(); };" 334 "function f3(x, y, z) { this.result = new f4(x + y + z).result; };" 335 "function f2(x, y) {" 336 " this.result = x + new f3(y + 1, y + 1, y + 1).result + y; };" 337 "function f1(x) { this.result = new f2(x + 1, x + 1).result + x; };" 338 "result = new f1(1).result;"); 339 } 340 NonIncrementalGC(CcTest::i_isolate()); 341 342 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 343 CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value()); 344 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 345 } 346 347 348 UNINITIALIZED_TEST(DeoptimizeBinaryOperationADDString) { 349 i::FLAG_turbo_deoptimization = true; 350 i::FLAG_concurrent_recompilation = false; 351 AllowNativesSyntaxNoInlining options; 352 v8::Isolate* isolate = v8::Isolate::New(); 353 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 354 isolate->Enter(); 355 { 356 LocalContext env(isolate); 357 v8::HandleScope scope(env->GetIsolate()); 358 359 const char* f_source = "function f(x, y) { return x + y; };"; 360 361 { 362 // Compile function f and collect to type feedback to insert binary op 363 // stub call in the optimized code. 364 i::FLAG_prepare_always_opt = true; 365 CompileRun( 366 "var count = 0;" 367 "var result = 0;" 368 "var deopt = false;" 369 "function X() { };" 370 "X.prototype.toString = function () {" 371 " if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'" 372 "};"); 373 CompileRun(f_source); 374 CompileRun( 375 "for (var i = 0; i < 5; i++) {" 376 " f('a+', new X());" 377 "};"); 378 379 // Compile an optimized version of f. 380 i::FLAG_always_opt = true; 381 CompileRun(f_source); 382 CompileRun("f('a+', new X());"); 383 CHECK(!i_isolate->use_crankshaft() || 384 GetJSFunction(env->Global(), "f")->IsOptimized()); 385 386 // Call f and force deoptimization while processing the binary operation. 387 CompileRun( 388 "deopt = true;" 389 "var result = f('a+', new X());"); 390 } 391 NonIncrementalGC(i_isolate); 392 393 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 394 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 395 v8::Handle<v8::Value> result = env->Global()->Get(v8_str("result")); 396 CHECK(result->IsString()); 397 v8::String::Utf8Value utf8(result); 398 CHECK_EQ("a+an X", *utf8); 399 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 400 } 401 isolate->Exit(); 402 isolate->Dispose(); 403 } 404 405 406 static void CompileConstructorWithDeoptimizingValueOf() { 407 CompileRun("var count = 0;" 408 "var result = 0;" 409 "var deopt = false;" 410 "function X() { };" 411 "X.prototype.valueOf = function () {" 412 " if (deopt) { count++; %DeoptimizeFunction(f); } return 8" 413 "};"); 414 } 415 416 417 static void TestDeoptimizeBinaryOpHelper(LocalContext* env, 418 const char* binary_op) { 419 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>((*env)->GetIsolate()); 420 EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer; 421 SNPrintF(f_source_buffer, 422 "function f(x, y) { return x %s y; };", 423 binary_op); 424 char* f_source = f_source_buffer.start(); 425 426 AllowNativesSyntaxNoInlining options; 427 // Compile function f and collect to type feedback to insert binary op stub 428 // call in the optimized code. 429 i::FLAG_prepare_always_opt = true; 430 CompileConstructorWithDeoptimizingValueOf(); 431 CompileRun(f_source); 432 CompileRun("for (var i = 0; i < 5; i++) {" 433 " f(8, new X());" 434 "};"); 435 436 // Compile an optimized version of f. 437 i::FLAG_always_opt = true; 438 CompileRun(f_source); 439 CompileRun("f(7, new X());"); 440 CHECK(!i_isolate->use_crankshaft() || 441 GetJSFunction((*env)->Global(), "f")->IsOptimized()); 442 443 // Call f and force deoptimization while processing the binary operation. 444 CompileRun("deopt = true;" 445 "var result = f(7, new X());"); 446 NonIncrementalGC(i_isolate); 447 CHECK(!GetJSFunction((*env)->Global(), "f")->IsOptimized()); 448 } 449 450 451 UNINITIALIZED_TEST(DeoptimizeBinaryOperationADD) { 452 i::FLAG_turbo_deoptimization = true; 453 i::FLAG_concurrent_recompilation = false; 454 v8::Isolate* isolate = v8::Isolate::New(); 455 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 456 isolate->Enter(); 457 { 458 LocalContext env(isolate); 459 v8::HandleScope scope(env->GetIsolate()); 460 461 TestDeoptimizeBinaryOpHelper(&env, "+"); 462 463 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 464 CHECK_EQ(15, env->Global()->Get(v8_str("result"))->Int32Value()); 465 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 466 } 467 isolate->Exit(); 468 isolate->Dispose(); 469 } 470 471 472 UNINITIALIZED_TEST(DeoptimizeBinaryOperationSUB) { 473 i::FLAG_turbo_deoptimization = true; 474 i::FLAG_concurrent_recompilation = false; 475 v8::Isolate* isolate = v8::Isolate::New(); 476 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 477 isolate->Enter(); 478 { 479 LocalContext env(isolate); 480 v8::HandleScope scope(env->GetIsolate()); 481 482 TestDeoptimizeBinaryOpHelper(&env, "-"); 483 484 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 485 CHECK_EQ(-1, env->Global()->Get(v8_str("result"))->Int32Value()); 486 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 487 } 488 isolate->Exit(); 489 isolate->Dispose(); 490 } 491 492 493 UNINITIALIZED_TEST(DeoptimizeBinaryOperationMUL) { 494 i::FLAG_turbo_deoptimization = true; 495 i::FLAG_concurrent_recompilation = false; 496 v8::Isolate* isolate = v8::Isolate::New(); 497 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 498 isolate->Enter(); 499 { 500 LocalContext env(isolate); 501 v8::HandleScope scope(env->GetIsolate()); 502 503 TestDeoptimizeBinaryOpHelper(&env, "*"); 504 505 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 506 CHECK_EQ(56, env->Global()->Get(v8_str("result"))->Int32Value()); 507 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 508 } 509 isolate->Exit(); 510 isolate->Dispose(); 511 } 512 513 514 UNINITIALIZED_TEST(DeoptimizeBinaryOperationDIV) { 515 i::FLAG_turbo_deoptimization = true; 516 i::FLAG_concurrent_recompilation = false; 517 v8::Isolate* isolate = v8::Isolate::New(); 518 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 519 isolate->Enter(); 520 { 521 LocalContext env(isolate); 522 v8::HandleScope scope(env->GetIsolate()); 523 524 TestDeoptimizeBinaryOpHelper(&env, "/"); 525 526 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 527 CHECK_EQ(0, env->Global()->Get(v8_str("result"))->Int32Value()); 528 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 529 } 530 isolate->Exit(); 531 isolate->Dispose(); 532 } 533 534 535 UNINITIALIZED_TEST(DeoptimizeBinaryOperationMOD) { 536 i::FLAG_turbo_deoptimization = true; 537 i::FLAG_concurrent_recompilation = false; 538 v8::Isolate* isolate = v8::Isolate::New(); 539 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 540 isolate->Enter(); 541 { 542 LocalContext env(isolate); 543 v8::HandleScope scope(env->GetIsolate()); 544 545 TestDeoptimizeBinaryOpHelper(&env, "%"); 546 547 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 548 CHECK_EQ(7, env->Global()->Get(v8_str("result"))->Int32Value()); 549 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 550 } 551 isolate->Exit(); 552 isolate->Dispose(); 553 } 554 555 556 UNINITIALIZED_TEST(DeoptimizeCompare) { 557 i::FLAG_turbo_deoptimization = true; 558 i::FLAG_concurrent_recompilation = false; 559 v8::Isolate* isolate = v8::Isolate::New(); 560 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 561 isolate->Enter(); 562 { 563 LocalContext env(isolate); 564 v8::HandleScope scope(env->GetIsolate()); 565 566 const char* f_source = "function f(x, y) { return x < y; };"; 567 568 { 569 AllowNativesSyntaxNoInlining options; 570 // Compile function f and collect to type feedback to insert compare ic 571 // call in the optimized code. 572 i::FLAG_prepare_always_opt = true; 573 CompileRun( 574 "var count = 0;" 575 "var result = 0;" 576 "var deopt = false;" 577 "function X() { };" 578 "X.prototype.toString = function () {" 579 " if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'" 580 "};"); 581 CompileRun(f_source); 582 CompileRun( 583 "for (var i = 0; i < 5; i++) {" 584 " f('a', new X());" 585 "};"); 586 587 // Compile an optimized version of f. 588 i::FLAG_always_opt = true; 589 CompileRun(f_source); 590 CompileRun("f('a', new X());"); 591 CHECK(!i_isolate->use_crankshaft() || 592 GetJSFunction(env->Global(), "f")->IsOptimized()); 593 594 // Call f and force deoptimization while processing the comparison. 595 CompileRun( 596 "deopt = true;" 597 "var result = f('a', new X());"); 598 } 599 NonIncrementalGC(i_isolate); 600 601 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 602 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 603 CHECK_EQ(true, env->Global()->Get(v8_str("result"))->BooleanValue()); 604 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate)); 605 } 606 isolate->Exit(); 607 isolate->Dispose(); 608 } 609 610 611 UNINITIALIZED_TEST(DeoptimizeLoadICStoreIC) { 612 i::FLAG_turbo_deoptimization = true; 613 i::FLAG_concurrent_recompilation = false; 614 v8::Isolate* isolate = v8::Isolate::New(); 615 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 616 isolate->Enter(); 617 { 618 LocalContext env(isolate); 619 v8::HandleScope scope(env->GetIsolate()); 620 621 // Functions to generate load/store/keyed load/keyed store IC calls. 622 const char* f1_source = "function f1(x) { return x.y; };"; 623 const char* g1_source = "function g1(x) { x.y = 1; };"; 624 const char* f2_source = "function f2(x, y) { return x[y]; };"; 625 const char* g2_source = "function g2(x, y) { x[y] = 1; };"; 626 627 { 628 AllowNativesSyntaxNoInlining options; 629 // Compile functions and collect to type feedback to insert ic 630 // calls in the optimized code. 631 i::FLAG_prepare_always_opt = true; 632 CompileRun( 633 "var count = 0;" 634 "var result = 0;" 635 "var deopt = false;" 636 "function X() { };" 637 "X.prototype.__defineGetter__('y', function () {" 638 " if (deopt) { count++; %DeoptimizeFunction(f1); };" 639 " return 13;" 640 "});" 641 "X.prototype.__defineSetter__('y', function () {" 642 " if (deopt) { count++; %DeoptimizeFunction(g1); };" 643 "});" 644 "X.prototype.__defineGetter__('z', function () {" 645 " if (deopt) { count++; %DeoptimizeFunction(f2); };" 646 " return 13;" 647 "});" 648 "X.prototype.__defineSetter__('z', function () {" 649 " if (deopt) { count++; %DeoptimizeFunction(g2); };" 650 "});"); 651 CompileRun(f1_source); 652 CompileRun(g1_source); 653 CompileRun(f2_source); 654 CompileRun(g2_source); 655 CompileRun( 656 "for (var i = 0; i < 5; i++) {" 657 " f1(new X());" 658 " g1(new X());" 659 " f2(new X(), 'z');" 660 " g2(new X(), 'z');" 661 "};"); 662 663 // Compile an optimized version of the functions. 664 i::FLAG_always_opt = true; 665 CompileRun(f1_source); 666 CompileRun(g1_source); 667 CompileRun(f2_source); 668 CompileRun(g2_source); 669 CompileRun("f1(new X());"); 670 CompileRun("g1(new X());"); 671 CompileRun("f2(new X(), 'z');"); 672 CompileRun("g2(new X(), 'z');"); 673 if (i_isolate->use_crankshaft()) { 674 CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized()); 675 CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized()); 676 CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized()); 677 CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized()); 678 } 679 680 // Call functions and force deoptimization while processing the ics. 681 CompileRun( 682 "deopt = true;" 683 "var result = f1(new X());" 684 "g1(new X());" 685 "f2(new X(), 'z');" 686 "g2(new X(), 'z');"); 687 } 688 NonIncrementalGC(i_isolate); 689 690 CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized()); 691 CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized()); 692 CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized()); 693 CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized()); 694 CHECK_EQ(4, env->Global()->Get(v8_str("count"))->Int32Value()); 695 CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value()); 696 } 697 isolate->Exit(); 698 isolate->Dispose(); 699 } 700 701 702 UNINITIALIZED_TEST(DeoptimizeLoadICStoreICNested) { 703 i::FLAG_turbo_deoptimization = true; 704 i::FLAG_concurrent_recompilation = false; 705 v8::Isolate* isolate = v8::Isolate::New(); 706 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 707 isolate->Enter(); 708 { 709 LocalContext env(isolate); 710 v8::HandleScope scope(env->GetIsolate()); 711 712 // Functions to generate load/store/keyed load/keyed store IC calls. 713 const char* f1_source = "function f1(x) { return x.y; };"; 714 const char* g1_source = "function g1(x) { x.y = 1; };"; 715 const char* f2_source = "function f2(x, y) { return x[y]; };"; 716 const char* g2_source = "function g2(x, y) { x[y] = 1; };"; 717 718 { 719 AllowNativesSyntaxNoInlining options; 720 // Compile functions and collect to type feedback to insert ic 721 // calls in the optimized code. 722 i::FLAG_prepare_always_opt = true; 723 CompileRun( 724 "var count = 0;" 725 "var result = 0;" 726 "var deopt = false;" 727 "function X() { };" 728 "X.prototype.__defineGetter__('y', function () {" 729 " g1(this);" 730 " return 13;" 731 "});" 732 "X.prototype.__defineSetter__('y', function () {" 733 " f2(this, 'z');" 734 "});" 735 "X.prototype.__defineGetter__('z', function () {" 736 " g2(this, 'z');" 737 "});" 738 "X.prototype.__defineSetter__('z', function () {" 739 " if (deopt) {" 740 " count++;" 741 " %DeoptimizeFunction(f1);" 742 " %DeoptimizeFunction(g1);" 743 " %DeoptimizeFunction(f2);" 744 " %DeoptimizeFunction(g2); };" 745 "});"); 746 CompileRun(f1_source); 747 CompileRun(g1_source); 748 CompileRun(f2_source); 749 CompileRun(g2_source); 750 CompileRun( 751 "for (var i = 0; i < 5; i++) {" 752 " f1(new X());" 753 " g1(new X());" 754 " f2(new X(), 'z');" 755 " g2(new X(), 'z');" 756 "};"); 757 758 // Compile an optimized version of the functions. 759 i::FLAG_always_opt = true; 760 CompileRun(f1_source); 761 CompileRun(g1_source); 762 CompileRun(f2_source); 763 CompileRun(g2_source); 764 CompileRun("f1(new X());"); 765 CompileRun("g1(new X());"); 766 CompileRun("f2(new X(), 'z');"); 767 CompileRun("g2(new X(), 'z');"); 768 if (i_isolate->use_crankshaft()) { 769 CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized()); 770 CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized()); 771 CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized()); 772 CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized()); 773 } 774 775 // Call functions and force deoptimization while processing the ics. 776 CompileRun( 777 "deopt = true;" 778 "var result = f1(new X());"); 779 } 780 NonIncrementalGC(i_isolate); 781 782 CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized()); 783 CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized()); 784 CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized()); 785 CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized()); 786 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 787 CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value()); 788 } 789 isolate->Exit(); 790 isolate->Dispose(); 791 } 792