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/compilation-cache.h" 34#include "src/debug.h" 35#include "src/deoptimizer.h" 36#include "src/isolate.h" 37#include "src/platform.h" 38#include "src/stub-cache.h" 39#include "test/cctest/cctest.h" 40 41using ::v8::internal::Deoptimizer; 42using ::v8::internal::EmbeddedVector; 43using ::v8::internal::Handle; 44using ::v8::internal::Isolate; 45using ::v8::internal::JSFunction; 46using ::v8::internal::OS; 47using ::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. 54class 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. 80class 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. 102static void NonIncrementalGC() { 103 CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 104} 105 106 107static 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 115TEST(DeoptimizeSimple) { 116 LocalContext env; 117 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 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(CcTest::i_isolate())); 150} 151 152 153TEST(DeoptimizeSimpleWithArguments) { 154 LocalContext env; 155 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 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(CcTest::i_isolate())); 189} 190 191 192TEST(DeoptimizeSimpleNested) { 193 LocalContext env; 194 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 213 } 214} 215 216 217TEST(DeoptimizeRecursive) { 218 LocalContext env; 219 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 237 238 v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast( 239 env->Global()->Get(v8::String::NewFromUtf8(CcTest::isolate(), "f"))); 240 CHECK(!fun.IsEmpty()); 241} 242 243 244TEST(DeoptimizeMultiple) { 245 LocalContext env; 246 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 269} 270 271 272TEST(DeoptimizeConstructor) { 273 LocalContext env; 274 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 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(CcTest::i_isolate())); 307} 308 309 310TEST(DeoptimizeConstructorMultiple) { 311 LocalContext env; 312 v8::HandleScope scope(env->GetIsolate()); 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(CcTest::i_isolate())); 336} 337 338 339TEST(DeoptimizeBinaryOperationADDString) { 340 i::FLAG_concurrent_recompilation = false; 341 AllowNativesSyntaxNoInlining options; 342 LocalContext env; 343 v8::HandleScope scope(env->GetIsolate()); 344 345 const char* f_source = "function f(x, y) { return x + y; };"; 346 347 { 348 // Compile function f and collect to type feedback to insert binary op stub 349 // call in the optimized code. 350 i::FLAG_prepare_always_opt = true; 351 CompileRun("var count = 0;" 352 "var result = 0;" 353 "var deopt = false;" 354 "function X() { };" 355 "X.prototype.toString = function () {" 356 " if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'" 357 "};"); 358 CompileRun(f_source); 359 CompileRun("for (var i = 0; i < 5; i++) {" 360 " f('a+', new X());" 361 "};"); 362 363 // Compile an optimized version of f. 364 i::FLAG_always_opt = true; 365 CompileRun(f_source); 366 CompileRun("f('a+', new X());"); 367 CHECK(!CcTest::i_isolate()->use_crankshaft() || 368 GetJSFunction(env->Global(), "f")->IsOptimized()); 369 370 // Call f and force deoptimization while processing the binary operation. 371 CompileRun("deopt = true;" 372 "var result = f('a+', new X());"); 373 } 374 NonIncrementalGC(); 375 376 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 377 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 378 v8::Handle<v8::Value> result = env->Global()->Get(v8_str("result")); 379 CHECK(result->IsString()); 380 v8::String::Utf8Value utf8(result); 381 CHECK_EQ("a+an X", *utf8); 382 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 383} 384 385 386static void CompileConstructorWithDeoptimizingValueOf() { 387 CompileRun("var count = 0;" 388 "var result = 0;" 389 "var deopt = false;" 390 "function X() { };" 391 "X.prototype.valueOf = function () {" 392 " if (deopt) { count++; %DeoptimizeFunction(f); } return 8" 393 "};"); 394} 395 396 397static void TestDeoptimizeBinaryOpHelper(LocalContext* env, 398 const char* binary_op) { 399 EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer; 400 SNPrintF(f_source_buffer, 401 "function f(x, y) { return x %s y; };", 402 binary_op); 403 char* f_source = f_source_buffer.start(); 404 405 AllowNativesSyntaxNoInlining options; 406 // Compile function f and collect to type feedback to insert binary op stub 407 // call in the optimized code. 408 i::FLAG_prepare_always_opt = true; 409 CompileConstructorWithDeoptimizingValueOf(); 410 CompileRun(f_source); 411 CompileRun("for (var i = 0; i < 5; i++) {" 412 " f(8, new X());" 413 "};"); 414 415 // Compile an optimized version of f. 416 i::FLAG_always_opt = true; 417 CompileRun(f_source); 418 CompileRun("f(7, new X());"); 419 CHECK(!CcTest::i_isolate()->use_crankshaft() || 420 GetJSFunction((*env)->Global(), "f")->IsOptimized()); 421 422 // Call f and force deoptimization while processing the binary operation. 423 CompileRun("deopt = true;" 424 "var result = f(7, new X());"); 425 NonIncrementalGC(); 426 CHECK(!GetJSFunction((*env)->Global(), "f")->IsOptimized()); 427} 428 429 430TEST(DeoptimizeBinaryOperationADD) { 431 i::FLAG_concurrent_recompilation = false; 432 LocalContext env; 433 v8::HandleScope scope(env->GetIsolate()); 434 435 TestDeoptimizeBinaryOpHelper(&env, "+"); 436 437 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 438 CHECK_EQ(15, env->Global()->Get(v8_str("result"))->Int32Value()); 439 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 440} 441 442 443TEST(DeoptimizeBinaryOperationSUB) { 444 i::FLAG_concurrent_recompilation = false; 445 LocalContext env; 446 v8::HandleScope scope(env->GetIsolate()); 447 448 TestDeoptimizeBinaryOpHelper(&env, "-"); 449 450 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 451 CHECK_EQ(-1, env->Global()->Get(v8_str("result"))->Int32Value()); 452 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 453} 454 455 456TEST(DeoptimizeBinaryOperationMUL) { 457 i::FLAG_concurrent_recompilation = false; 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(CcTest::i_isolate())); 466} 467 468 469TEST(DeoptimizeBinaryOperationDIV) { 470 i::FLAG_concurrent_recompilation = false; 471 LocalContext env; 472 v8::HandleScope scope(env->GetIsolate()); 473 474 TestDeoptimizeBinaryOpHelper(&env, "/"); 475 476 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 477 CHECK_EQ(0, env->Global()->Get(v8_str("result"))->Int32Value()); 478 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 479} 480 481 482TEST(DeoptimizeBinaryOperationMOD) { 483 i::FLAG_concurrent_recompilation = false; 484 LocalContext env; 485 v8::HandleScope scope(env->GetIsolate()); 486 487 TestDeoptimizeBinaryOpHelper(&env, "%"); 488 489 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 490 CHECK_EQ(7, env->Global()->Get(v8_str("result"))->Int32Value()); 491 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 492} 493 494 495TEST(DeoptimizeCompare) { 496 i::FLAG_concurrent_recompilation = false; 497 LocalContext env; 498 v8::HandleScope scope(env->GetIsolate()); 499 500 const char* f_source = "function f(x, y) { return x < y; };"; 501 502 { 503 AllowNativesSyntaxNoInlining options; 504 // Compile function f and collect to type feedback to insert compare ic 505 // call in the optimized code. 506 i::FLAG_prepare_always_opt = true; 507 CompileRun("var count = 0;" 508 "var result = 0;" 509 "var deopt = false;" 510 "function X() { };" 511 "X.prototype.toString = function () {" 512 " if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'" 513 "};"); 514 CompileRun(f_source); 515 CompileRun("for (var i = 0; i < 5; i++) {" 516 " f('a', new X());" 517 "};"); 518 519 // Compile an optimized version of f. 520 i::FLAG_always_opt = true; 521 CompileRun(f_source); 522 CompileRun("f('a', new X());"); 523 CHECK(!CcTest::i_isolate()->use_crankshaft() || 524 GetJSFunction(env->Global(), "f")->IsOptimized()); 525 526 // Call f and force deoptimization while processing the comparison. 527 CompileRun("deopt = true;" 528 "var result = f('a', new X());"); 529 } 530 NonIncrementalGC(); 531 532 CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized()); 533 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 534 CHECK_EQ(true, env->Global()->Get(v8_str("result"))->BooleanValue()); 535 CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate())); 536} 537 538 539TEST(DeoptimizeLoadICStoreIC) { 540 i::FLAG_concurrent_recompilation = false; 541 LocalContext env; 542 v8::HandleScope scope(env->GetIsolate()); 543 544 // Functions to generate load/store/keyed load/keyed store IC calls. 545 const char* f1_source = "function f1(x) { return x.y; };"; 546 const char* g1_source = "function g1(x) { x.y = 1; };"; 547 const char* f2_source = "function f2(x, y) { return x[y]; };"; 548 const char* g2_source = "function g2(x, y) { x[y] = 1; };"; 549 550 { 551 AllowNativesSyntaxNoInlining options; 552 // Compile functions and collect to type feedback to insert ic 553 // calls in the optimized code. 554 i::FLAG_prepare_always_opt = true; 555 CompileRun("var count = 0;" 556 "var result = 0;" 557 "var deopt = false;" 558 "function X() { };" 559 "X.prototype.__defineGetter__('y', function () {" 560 " if (deopt) { count++; %DeoptimizeFunction(f1); };" 561 " return 13;" 562 "});" 563 "X.prototype.__defineSetter__('y', function () {" 564 " if (deopt) { count++; %DeoptimizeFunction(g1); };" 565 "});" 566 "X.prototype.__defineGetter__('z', function () {" 567 " if (deopt) { count++; %DeoptimizeFunction(f2); };" 568 " return 13;" 569 "});" 570 "X.prototype.__defineSetter__('z', function () {" 571 " if (deopt) { count++; %DeoptimizeFunction(g2); };" 572 "});"); 573 CompileRun(f1_source); 574 CompileRun(g1_source); 575 CompileRun(f2_source); 576 CompileRun(g2_source); 577 CompileRun("for (var i = 0; i < 5; i++) {" 578 " f1(new X());" 579 " g1(new X());" 580 " f2(new X(), 'z');" 581 " g2(new X(), 'z');" 582 "};"); 583 584 // Compile an optimized version of the functions. 585 i::FLAG_always_opt = true; 586 CompileRun(f1_source); 587 CompileRun(g1_source); 588 CompileRun(f2_source); 589 CompileRun(g2_source); 590 CompileRun("f1(new X());"); 591 CompileRun("g1(new X());"); 592 CompileRun("f2(new X(), 'z');"); 593 CompileRun("g2(new X(), 'z');"); 594 if (CcTest::i_isolate()->use_crankshaft()) { 595 CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized()); 596 CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized()); 597 CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized()); 598 CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized()); 599 } 600 601 // Call functions and force deoptimization while processing the ics. 602 CompileRun("deopt = true;" 603 "var result = f1(new X());" 604 "g1(new X());" 605 "f2(new X(), 'z');" 606 "g2(new X(), 'z');"); 607 } 608 NonIncrementalGC(); 609 610 CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized()); 611 CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized()); 612 CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized()); 613 CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized()); 614 CHECK_EQ(4, env->Global()->Get(v8_str("count"))->Int32Value()); 615 CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value()); 616} 617 618 619TEST(DeoptimizeLoadICStoreICNested) { 620 i::FLAG_concurrent_recompilation = false; 621 LocalContext env; 622 v8::HandleScope scope(env->GetIsolate()); 623 624 // Functions to generate load/store/keyed load/keyed store IC calls. 625 const char* f1_source = "function f1(x) { return x.y; };"; 626 const char* g1_source = "function g1(x) { x.y = 1; };"; 627 const char* f2_source = "function f2(x, y) { return x[y]; };"; 628 const char* g2_source = "function g2(x, y) { x[y] = 1; };"; 629 630 { 631 AllowNativesSyntaxNoInlining options; 632 // Compile functions and collect to type feedback to insert ic 633 // calls in the optimized code. 634 i::FLAG_prepare_always_opt = true; 635 CompileRun("var count = 0;" 636 "var result = 0;" 637 "var deopt = false;" 638 "function X() { };" 639 "X.prototype.__defineGetter__('y', function () {" 640 " g1(this);" 641 " return 13;" 642 "});" 643 "X.prototype.__defineSetter__('y', function () {" 644 " f2(this, 'z');" 645 "});" 646 "X.prototype.__defineGetter__('z', function () {" 647 " g2(this, 'z');" 648 "});" 649 "X.prototype.__defineSetter__('z', function () {" 650 " if (deopt) {" 651 " count++;" 652 " %DeoptimizeFunction(f1);" 653 " %DeoptimizeFunction(g1);" 654 " %DeoptimizeFunction(f2);" 655 " %DeoptimizeFunction(g2); };" 656 "});"); 657 CompileRun(f1_source); 658 CompileRun(g1_source); 659 CompileRun(f2_source); 660 CompileRun(g2_source); 661 CompileRun("for (var i = 0; i < 5; i++) {" 662 " f1(new X());" 663 " g1(new X());" 664 " f2(new X(), 'z');" 665 " g2(new X(), 'z');" 666 "};"); 667 668 // Compile an optimized version of the functions. 669 i::FLAG_always_opt = true; 670 CompileRun(f1_source); 671 CompileRun(g1_source); 672 CompileRun(f2_source); 673 CompileRun(g2_source); 674 CompileRun("f1(new X());"); 675 CompileRun("g1(new X());"); 676 CompileRun("f2(new X(), 'z');"); 677 CompileRun("g2(new X(), 'z');"); 678 if (CcTest::i_isolate()->use_crankshaft()) { 679 CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized()); 680 CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized()); 681 CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized()); 682 CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized()); 683 } 684 685 // Call functions and force deoptimization while processing the ics. 686 CompileRun("deopt = true;" 687 "var result = f1(new X());"); 688 } 689 NonIncrementalGC(); 690 691 CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized()); 692 CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized()); 693 CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized()); 694 CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized()); 695 CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value()); 696 CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value()); 697} 698