1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/frames-inl.h" 6 #include "test/cctest/compiler/function-tester.h" 7 8 namespace v8 { 9 namespace internal { 10 namespace compiler { 11 12 namespace { 13 14 // Helper to determine inline count via JavaScriptFrame::GetFunctions. 15 // Note that a count of 1 indicates that no inlining has occured. 16 void AssertInlineCount(const v8::FunctionCallbackInfo<v8::Value>& args) { 17 StackTraceFrameIterator it(CcTest::i_isolate()); 18 int frames_seen = 0; 19 JavaScriptFrame* topmost = it.frame(); 20 while (!it.done()) { 21 JavaScriptFrame* frame = it.frame(); 22 List<JSFunction*> functions(2); 23 frame->GetFunctions(&functions); 24 PrintF("%d %s, inline count: %d\n", frames_seen, 25 frame->function()->shared()->DebugName()->ToCString().get(), 26 functions.length()); 27 frames_seen++; 28 it.Advance(); 29 } 30 List<JSFunction*> functions(2); 31 topmost->GetFunctions(&functions); 32 CHECK_EQ(args[0] 33 ->ToInt32(args.GetIsolate()->GetCurrentContext()) 34 .ToLocalChecked() 35 ->Value(), 36 functions.length()); 37 } 38 39 40 void InstallAssertInlineCountHelper(v8::Isolate* isolate) { 41 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 42 v8::Local<v8::FunctionTemplate> t = 43 v8::FunctionTemplate::New(isolate, AssertInlineCount); 44 CHECK(context->Global() 45 ->Set(context, v8_str("AssertInlineCount"), 46 t->GetFunction(context).ToLocalChecked()) 47 .FromJust()); 48 } 49 50 51 const uint32_t kRestrictedInliningFlags = 52 CompilationInfo::kFunctionContextSpecializing | 53 CompilationInfo::kTypingEnabled; 54 55 const uint32_t kInlineFlags = CompilationInfo::kInliningEnabled | 56 CompilationInfo::kFunctionContextSpecializing | 57 CompilationInfo::kTypingEnabled; 58 59 } // namespace 60 61 62 TEST(SimpleInlining) { 63 FunctionTester T( 64 "(function(){" 65 " function foo(s) { AssertInlineCount(2); return s; };" 66 " function bar(s, t) { return foo(s); };" 67 " return bar;" 68 "})();", 69 kInlineFlags); 70 71 InstallAssertInlineCountHelper(CcTest::isolate()); 72 T.CheckCall(T.Val(1), T.Val(1), T.Val(2)); 73 } 74 75 76 TEST(SimpleInliningDeopt) { 77 FunctionTester T( 78 "(function(){" 79 " function foo(s) { %DeoptimizeFunction(bar); return s; };" 80 " function bar(s, t) { return foo(s); };" 81 " return bar;" 82 "})();", 83 kInlineFlags); 84 85 InstallAssertInlineCountHelper(CcTest::isolate()); 86 T.CheckCall(T.Val(1), T.Val(1), T.Val(2)); 87 } 88 89 90 TEST(SimpleInliningDeoptSelf) { 91 FunctionTester T( 92 "(function(){" 93 " function foo(s) { %_DeoptimizeNow(); return s; };" 94 " function bar(s, t) { return foo(s); };" 95 " return bar;" 96 "})();", 97 kInlineFlags); 98 99 InstallAssertInlineCountHelper(CcTest::isolate()); 100 T.CheckCall(T.Val(1), T.Val(1), T.Val(2)); 101 } 102 103 104 TEST(SimpleInliningContext) { 105 FunctionTester T( 106 "(function () {" 107 " function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };" 108 " function bar(s, t) { return foo(s); };" 109 " return bar;" 110 "})();", 111 kInlineFlags); 112 113 InstallAssertInlineCountHelper(CcTest::isolate()); 114 T.CheckCall(T.Val(13), T.Val(1), T.Val(2)); 115 } 116 117 118 TEST(SimpleInliningContextDeopt) { 119 FunctionTester T( 120 "(function () {" 121 " function foo(s) {" 122 " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;" 123 " return s + x;" 124 " };" 125 " function bar(s, t) { return foo(s); };" 126 " return bar;" 127 "})();", 128 kInlineFlags); 129 130 InstallAssertInlineCountHelper(CcTest::isolate()); 131 T.CheckCall(T.Val(13), T.Val(1), T.Val(2)); 132 } 133 134 135 TEST(CaptureContext) { 136 FunctionTester T( 137 "var f = (function () {" 138 " var x = 42;" 139 " function bar(s) { return x + s; };" 140 " return (function (s) { return bar(s); });" 141 "})();" 142 "(function (s) { return f(s) })", 143 kInlineFlags); 144 145 InstallAssertInlineCountHelper(CcTest::isolate()); 146 T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined()); 147 } 148 149 150 // TODO(sigurds) For now we do not inline any native functions. If we do at 151 // some point, change this test. 152 TEST(DontInlineEval) { 153 FunctionTester T( 154 "var x = 42;" 155 "(function () {" 156 " function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };" 157 " return bar;" 158 "})();", 159 kInlineFlags); 160 161 InstallAssertInlineCountHelper(CcTest::isolate()); 162 T.CheckCall(T.Val(42), T.Val("x"), T.undefined()); 163 } 164 165 166 TEST(InlineOmitArguments) { 167 FunctionTester T( 168 "(function () {" 169 " var x = 42;" 170 " function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };" 171 " function foo(s, t) { return bar(s); };" 172 " return foo;" 173 "})();", 174 kInlineFlags); 175 176 InstallAssertInlineCountHelper(CcTest::isolate()); 177 T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined()); 178 } 179 180 181 TEST(InlineOmitArgumentsObject) { 182 FunctionTester T( 183 "(function () {" 184 " function bar(s, t, u, v) { AssertInlineCount(2); return arguments; };" 185 " function foo(s, t) { var args = bar(s);" 186 " return args.length == 1 &&" 187 " args[0] == 11; };" 188 " return foo;" 189 "})();", 190 kInlineFlags); 191 192 InstallAssertInlineCountHelper(CcTest::isolate()); 193 T.CheckCall(T.true_value(), T.Val(11), T.undefined()); 194 } 195 196 197 TEST(InlineOmitArgumentsDeopt) { 198 FunctionTester T( 199 "(function () {" 200 " function foo(s,t,u,v) { AssertInlineCount(2);" 201 " %DeoptimizeFunction(bar); return baz(); };" 202 " function bar() { return foo(11); };" 203 " function baz() { return foo.arguments.length == 1 &&" 204 " foo.arguments[0] == 11; }" 205 " return bar;" 206 "})();", 207 kInlineFlags); 208 209 InstallAssertInlineCountHelper(CcTest::isolate()); 210 T.CheckCall(T.true_value(), T.Val(12), T.Val(14)); 211 } 212 213 214 TEST(InlineSurplusArguments) { 215 FunctionTester T( 216 "(function () {" 217 " var x = 42;" 218 " function foo(s) { AssertInlineCount(2); return x + s; };" 219 " function bar(s, t) { return foo(s, t, 13); };" 220 " return bar;" 221 "})();", 222 kInlineFlags); 223 224 InstallAssertInlineCountHelper(CcTest::isolate()); 225 T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined()); 226 } 227 228 229 TEST(InlineSurplusArgumentsObject) { 230 FunctionTester T( 231 "(function () {" 232 " function foo(s) { AssertInlineCount(2); return arguments; };" 233 " function bar(s, t) { var args = foo(s, t, 13);" 234 " return args.length == 3 &&" 235 " args[0] == 11 &&" 236 " args[1] == 12 &&" 237 " args[2] == 13; };" 238 " return bar;" 239 "})();", 240 kInlineFlags); 241 242 InstallAssertInlineCountHelper(CcTest::isolate()); 243 T.CheckCall(T.true_value(), T.Val(11), T.Val(12)); 244 } 245 246 247 TEST(InlineSurplusArgumentsDeopt) { 248 FunctionTester T( 249 "(function () {" 250 " function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar);" 251 " return baz(); };" 252 " function bar() { return foo(13, 14, 15); };" 253 " function baz() { return foo.arguments.length == 3 &&" 254 " foo.arguments[0] == 13 &&" 255 " foo.arguments[1] == 14 &&" 256 " foo.arguments[2] == 15; }" 257 " return bar;" 258 "})();", 259 kInlineFlags); 260 261 InstallAssertInlineCountHelper(CcTest::isolate()); 262 T.CheckCall(T.true_value(), T.Val(12), T.Val(14)); 263 } 264 265 266 TEST(InlineTwice) { 267 FunctionTester T( 268 "(function () {" 269 " var x = 42;" 270 " function bar(s) { AssertInlineCount(2); return x + s; };" 271 " return (function (s,t) { return bar(s) + bar(t); });" 272 "})();", 273 kInlineFlags); 274 275 InstallAssertInlineCountHelper(CcTest::isolate()); 276 T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4)); 277 } 278 279 280 TEST(InlineTwiceDependent) { 281 FunctionTester T( 282 "(function () {" 283 " var x = 42;" 284 " function foo(s) { AssertInlineCount(2); return x + s; };" 285 " function bar(s,t) { return foo(foo(s)); };" 286 " return bar;" 287 "})();", 288 kInlineFlags); 289 290 InstallAssertInlineCountHelper(CcTest::isolate()); 291 T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4)); 292 } 293 294 295 TEST(InlineTwiceDependentDiamond) { 296 FunctionTester T( 297 "(function () {" 298 " var x = 41;" 299 " function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {" 300 " return x - s } else { return x + s; } };" 301 " function bar(s,t) { return foo(foo(s)); };" 302 " return bar;" 303 "})();", 304 kInlineFlags); 305 306 InstallAssertInlineCountHelper(CcTest::isolate()); 307 T.CheckCall(T.Val(-11), T.Val(11), T.Val(4)); 308 } 309 310 311 TEST(InlineTwiceDependentDiamondDifferent) { 312 FunctionTester T( 313 "(function () {" 314 " var x = 41;" 315 " function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {" 316 " return x - s * t } else { return x + s * t; } };" 317 " function bar(s,t) { return foo(foo(s, 3), 5); };" 318 " return bar;" 319 "})();", 320 kInlineFlags); 321 322 InstallAssertInlineCountHelper(CcTest::isolate()); 323 T.CheckCall(T.Val(-329), T.Val(11), T.Val(4)); 324 } 325 326 327 TEST(InlineLoopGuardedEmpty) { 328 FunctionTester T( 329 "(function () {" 330 " function foo(s) { AssertInlineCount(2); if (s) while (s); return s; };" 331 " function bar(s,t) { return foo(s); };" 332 " return bar;" 333 "})();", 334 kInlineFlags); 335 336 InstallAssertInlineCountHelper(CcTest::isolate()); 337 T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4)); 338 } 339 340 341 TEST(InlineLoopGuardedOnce) { 342 FunctionTester T( 343 "(function () {" 344 " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {" 345 " s = s - 1; }; return s; };" 346 " function bar(s,t) { return foo(s,t); };" 347 " return bar;" 348 "})();", 349 kInlineFlags); 350 351 InstallAssertInlineCountHelper(CcTest::isolate()); 352 T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4)); 353 } 354 355 356 TEST(InlineLoopGuardedTwice) { 357 FunctionTester T( 358 "(function () {" 359 " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {" 360 " s = s - 1; }; return s; };" 361 " function bar(s,t) { return foo(foo(s,t),t); };" 362 " return bar;" 363 "})();", 364 kInlineFlags); 365 366 InstallAssertInlineCountHelper(CcTest::isolate()); 367 T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4)); 368 } 369 370 371 TEST(InlineLoopUnguardedEmpty) { 372 FunctionTester T( 373 "(function () {" 374 " function foo(s) { AssertInlineCount(2); while (s); return s; };" 375 " function bar(s, t) { return foo(s); };" 376 " return bar;" 377 "})();", 378 kInlineFlags); 379 380 InstallAssertInlineCountHelper(CcTest::isolate()); 381 T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4)); 382 } 383 384 385 TEST(InlineLoopUnguardedOnce) { 386 FunctionTester T( 387 "(function () {" 388 " function foo(s) { AssertInlineCount(2); while (s) {" 389 " s = s - 1; }; return s; };" 390 " function bar(s, t) { return foo(s); };" 391 " return bar;" 392 "})();", 393 kInlineFlags); 394 395 InstallAssertInlineCountHelper(CcTest::isolate()); 396 T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4)); 397 } 398 399 400 TEST(InlineLoopUnguardedTwice) { 401 FunctionTester T( 402 "(function () {" 403 " function foo(s) { AssertInlineCount(2); while (s > 0) {" 404 " s = s - 1; }; return s; };" 405 " function bar(s,t) { return foo(foo(s,t),t); };" 406 " return bar;" 407 "})();", 408 kInlineFlags); 409 410 InstallAssertInlineCountHelper(CcTest::isolate()); 411 T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4)); 412 } 413 414 415 TEST(InlineStrictIntoNonStrict) { 416 FunctionTester T( 417 "(function () {" 418 " var x = Object.create({}, { y: { value:42, writable:false } });" 419 " function foo(s) { 'use strict';" 420 " x.y = 9; };" 421 " function bar(s,t) { return foo(s); };" 422 " return bar;" 423 "})();", 424 kInlineFlags); 425 426 InstallAssertInlineCountHelper(CcTest::isolate()); 427 T.CheckThrows(T.undefined(), T.undefined()); 428 } 429 430 431 TEST(InlineNonStrictIntoStrict) { 432 FunctionTester T( 433 "(function () {" 434 " var x = Object.create({}, { y: { value:42, writable:false } });" 435 " function foo(s) { x.y = 9; return x.y; };" 436 " function bar(s,t) { \'use strict\'; return foo(s); };" 437 " return bar;" 438 "})();", 439 kInlineFlags); 440 441 InstallAssertInlineCountHelper(CcTest::isolate()); 442 T.CheckCall(T.Val(42), T.undefined(), T.undefined()); 443 } 444 445 446 TEST(InlineIntrinsicIsSmi) { 447 FunctionTester T( 448 "(function () {" 449 " var x = 42;" 450 " function bar(s,t) { return %_IsSmi(x); };" 451 " return bar;" 452 "})();", 453 kInlineFlags); 454 455 InstallAssertInlineCountHelper(CcTest::isolate()); 456 T.CheckCall(T.true_value(), T.Val(12), T.Val(4)); 457 } 458 459 460 TEST(InlineIntrinsicIsArray) { 461 FunctionTester T( 462 "(function () {" 463 " var x = [1,2,3];" 464 " function bar(s,t) { return %_IsArray(x); };" 465 " return bar;" 466 "})();", 467 kInlineFlags); 468 469 InstallAssertInlineCountHelper(CcTest::isolate()); 470 T.CheckCall(T.true_value(), T.Val(12), T.Val(4)); 471 472 FunctionTester T2( 473 "(function () {" 474 " var x = 32;" 475 " function bar(s,t) { return %_IsArray(x); };" 476 " return bar;" 477 "})();", 478 kInlineFlags); 479 480 T2.CheckCall(T.false_value(), T.Val(12), T.Val(4)); 481 482 FunctionTester T3( 483 "(function () {" 484 " var x = bar;" 485 " function bar(s,t) { return %_IsArray(x); };" 486 " return bar;" 487 "})();", 488 kInlineFlags); 489 490 T3.CheckCall(T.false_value(), T.Val(12), T.Val(4)); 491 } 492 493 494 TEST(InlineWithArguments) { 495 FunctionTester T( 496 "(function () {" 497 " function foo(s,t,u) { AssertInlineCount(2);" 498 " return foo.arguments.length == 3 &&" 499 " foo.arguments[0] == 13 &&" 500 " foo.arguments[1] == 14 &&" 501 " foo.arguments[2] == 15;" 502 " }" 503 " function bar() { return foo(13, 14, 15); };" 504 " return bar;" 505 "})();", 506 kInlineFlags); 507 508 InstallAssertInlineCountHelper(CcTest::isolate()); 509 T.CheckCall(T.true_value(), T.Val(12), T.Val(14)); 510 } 511 512 513 TEST(InlineBuiltin) { 514 FunctionTester T( 515 "(function () {" 516 " function foo(s,t,u) { AssertInlineCount(2); return true; }" 517 " function bar() { return foo(); };" 518 " %SetForceInlineFlag(foo);" 519 " return bar;" 520 "})();", 521 kRestrictedInliningFlags); 522 523 InstallAssertInlineCountHelper(CcTest::isolate()); 524 T.CheckCall(T.true_value()); 525 } 526 527 528 TEST(InlineNestedBuiltin) { 529 FunctionTester T( 530 "(function () {" 531 " function foo(s,t,u) { AssertInlineCount(3); return true; }" 532 " function baz(s,t,u) { return foo(s,t,u); }" 533 " function bar() { return baz(); };" 534 " %SetForceInlineFlag(foo);" 535 " %SetForceInlineFlag(baz);" 536 " return bar;" 537 "})();", 538 kRestrictedInliningFlags); 539 540 InstallAssertInlineCountHelper(CcTest::isolate()); 541 T.CheckCall(T.true_value()); 542 } 543 544 545 TEST(StrongModeArity) { 546 FLAG_strong_mode = true; 547 FunctionTester T( 548 "(function () {" 549 " function foo(x, y) { 'use strong'; return x; }" 550 " function bar(x, y) { return foo(x); }" 551 " return bar;" 552 "})();", 553 kInlineFlags); 554 T.CheckThrows(T.undefined(), T.undefined()); 555 } 556 557 558 TEST(StrongModeArityOuter) { 559 FLAG_strong_mode = true; 560 FunctionTester T( 561 "(function () {" 562 " 'use strong';" 563 " function foo(x, y) { return x; }" 564 " function bar(x, y) { return foo(x); }" 565 " return bar;" 566 "})();", 567 kInlineFlags); 568 T.CheckThrows(T.undefined(), T.undefined()); 569 } 570 571 572 TEST(InlineSelfRecursive) { 573 FunctionTester T( 574 "(function () {" 575 " function foo(x) { " 576 " AssertInlineCount(1);" 577 " if (x == 1) return foo(12);" 578 " return x;" 579 " }" 580 " return foo;" 581 "})();", 582 kInlineFlags); 583 584 InstallAssertInlineCountHelper(CcTest::isolate()); 585 T.CheckCall(T.Val(12), T.Val(1)); 586 } 587 588 589 TEST(InlineMutuallyRecursive) { 590 FunctionTester T( 591 "(function () {" 592 " function bar(x) { AssertInlineCount(2); return foo(x); }" 593 " function foo(x) { " 594 " if (x == 1) return bar(42);" 595 " return x;" 596 " }" 597 " return foo;" 598 "})();", 599 kInlineFlags); 600 601 InstallAssertInlineCountHelper(CcTest::isolate()); 602 T.CheckCall(T.Val(42), T.Val(1)); 603 } 604 605 } // namespace compiler 606 } // namespace internal 607 } // namespace v8 608