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