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