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