1 //===- FunctionPropertiesAnalysisTest.cpp - Function Properties Unit Tests-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Analysis/FunctionPropertiesAnalysis.h" 10 #include "llvm/ADT/iterator_range.h" 11 #include "llvm/Analysis/AliasAnalysis.h" 12 #include "llvm/Analysis/LoopInfo.h" 13 #include "llvm/AsmParser/Parser.h" 14 #include "llvm/IR/Dominators.h" 15 #include "llvm/IR/Instructions.h" 16 #include "llvm/IR/LLVMContext.h" 17 #include "llvm/IR/Module.h" 18 #include "llvm/IR/PassManager.h" 19 #include "llvm/Passes/PassBuilder.h" 20 #include "llvm/Passes/StandardInstrumentations.h" 21 #include "llvm/Support/SourceMgr.h" 22 #include "llvm/Transforms/Utils/Cloning.h" 23 #include "gtest/gtest.h" 24 #include <cstring> 25 26 using namespace llvm; 27 28 extern cl::opt<bool> EnableDetailedFunctionProperties; 29 extern cl::opt<bool> BigBasicBlockInstructionThreshold; 30 extern cl::opt<bool> MediumBasicBlockInstrutionThreshold; 31 32 namespace { 33 34 class FunctionPropertiesAnalysisTest : public testing::Test { 35 public: 36 FunctionPropertiesAnalysisTest() { 37 FAM.registerPass([&] { return DominatorTreeAnalysis(); }); 38 FAM.registerPass([&] { return LoopAnalysis(); }); 39 FAM.registerPass([&] { return PassInstrumentationAnalysis(); }); 40 } 41 42 protected: 43 std::unique_ptr<DominatorTree> DT; 44 std::unique_ptr<LoopInfo> LI; 45 FunctionAnalysisManager FAM; 46 47 FunctionPropertiesInfo buildFPI(Function &F) { 48 return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM); 49 } 50 51 void invalidate(Function &F) { 52 PreservedAnalyses PA = PreservedAnalyses::none(); 53 FAM.invalidate(F, PA); 54 } 55 56 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) { 57 SMDiagnostic Err; 58 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); 59 if (!Mod) 60 Err.print("MLAnalysisTests", errs()); 61 return Mod; 62 } 63 64 CallBase* findCall(Function& F, const char* Name = nullptr) { 65 for (auto &BB : F) 66 for (auto &I : BB ) 67 if (auto *CB = dyn_cast<CallBase>(&I)) 68 if (!Name || CB->getName() == Name) 69 return CB; 70 return nullptr; 71 } 72 }; 73 74 TEST_F(FunctionPropertiesAnalysisTest, BasicTest) { 75 LLVMContext C; 76 std::unique_ptr<Module> M = makeLLVMModule(C, 77 R"IR( 78 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 79 target triple = "x86_64-pc-linux-gnu" 80 declare i32 @f1(i32) 81 declare i32 @f2(i32) 82 define i32 @branches(i32) { 83 %cond = icmp slt i32 %0, 3 84 br i1 %cond, label %then, label %else 85 then: 86 %ret.1 = call i32 @f1(i32 %0) 87 br label %last.block 88 else: 89 %ret.2 = call i32 @f2(i32 %0) 90 br label %last.block 91 last.block: 92 %ret = phi i32 [%ret.1, %then], [%ret.2, %else] 93 ret i32 %ret 94 } 95 define internal i32 @top() { 96 %1 = call i32 @branches(i32 2) 97 %2 = call i32 @f1(i32 %1) 98 ret i32 %2 99 } 100 )IR"); 101 102 Function *BranchesFunction = M->getFunction("branches"); 103 FunctionPropertiesInfo BranchesFeatures = buildFPI(*BranchesFunction); 104 EXPECT_EQ(BranchesFeatures.BasicBlockCount, 4); 105 EXPECT_EQ(BranchesFeatures.BlocksReachedFromConditionalInstruction, 2); 106 // 2 Users: top is one. The other is added because @branches is not internal, 107 // so it may have external callers. 108 EXPECT_EQ(BranchesFeatures.Uses, 2); 109 EXPECT_EQ(BranchesFeatures.DirectCallsToDefinedFunctions, 0); 110 EXPECT_EQ(BranchesFeatures.LoadInstCount, 0); 111 EXPECT_EQ(BranchesFeatures.StoreInstCount, 0); 112 EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0); 113 EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0); 114 115 Function *TopFunction = M->getFunction("top"); 116 FunctionPropertiesInfo TopFeatures = buildFPI(*TopFunction); 117 EXPECT_EQ(TopFeatures.BasicBlockCount, 1); 118 EXPECT_EQ(TopFeatures.BlocksReachedFromConditionalInstruction, 0); 119 EXPECT_EQ(TopFeatures.Uses, 0); 120 EXPECT_EQ(TopFeatures.DirectCallsToDefinedFunctions, 1); 121 EXPECT_EQ(BranchesFeatures.LoadInstCount, 0); 122 EXPECT_EQ(BranchesFeatures.StoreInstCount, 0); 123 EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0); 124 EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0); 125 126 EnableDetailedFunctionProperties.setValue(true); 127 FunctionPropertiesInfo DetailedBranchesFeatures = buildFPI(*BranchesFunction); 128 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSingleSuccessor, 2); 129 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoSuccessors, 1); 130 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoSuccessors, 0); 131 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSinglePredecessor, 2); 132 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoPredecessors, 1); 133 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoPredecessors, 0); 134 EXPECT_EQ(DetailedBranchesFeatures.BigBasicBlocks, 0); 135 EXPECT_EQ(DetailedBranchesFeatures.MediumBasicBlocks, 0); 136 EXPECT_EQ(DetailedBranchesFeatures.SmallBasicBlocks, 4); 137 EXPECT_EQ(DetailedBranchesFeatures.CastInstructionCount, 0); 138 EXPECT_EQ(DetailedBranchesFeatures.FloatingPointInstructionCount, 0); 139 EXPECT_EQ(DetailedBranchesFeatures.IntegerInstructionCount, 4); 140 EXPECT_EQ(DetailedBranchesFeatures.IntegerConstantCount, 1); 141 EXPECT_EQ(DetailedBranchesFeatures.FloatingPointConstantCount, 0); 142 EnableDetailedFunctionProperties.setValue(false); 143 } 144 145 TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) { 146 LLVMContext C; 147 std::unique_ptr<Module> M = makeLLVMModule(C, 148 R"IR( 149 define i64 @f1() { 150 br i1 0, label %br1, label %finally 151 br1: 152 ret i64 0 153 finally: 154 ret i64 3 155 } 156 )IR"); 157 158 Function *F1 = M->getFunction("f1"); 159 EnableDetailedFunctionProperties.setValue(true); 160 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); 161 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0); 162 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 1); 163 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0); 164 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 2); 165 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0); 166 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0); 167 EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0); 168 EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0); 169 EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 3); 170 EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0); 171 EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0); 172 EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 0); 173 EXPECT_EQ(DetailedF1Properties.IntegerConstantCount, 3); 174 EXPECT_EQ(DetailedF1Properties.FloatingPointConstantCount, 0); 175 EnableDetailedFunctionProperties.setValue(false); 176 } 177 178 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) { 179 LLVMContext C; 180 std::unique_ptr<Module> M = makeLLVMModule(C, 181 R"IR( 182 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 183 target triple = "x86_64-pc-linux-gnu" 184 define i32 @f1(i32 %a) { 185 %b = call i32 @f2(i32 %a) 186 %c = add i32 %b, 2 187 ret i32 %c 188 } 189 190 define i32 @f2(i32 %a) { 191 %b = add i32 %a, 1 192 ret i32 %b 193 } 194 )IR"); 195 196 Function *F1 = M->getFunction("f1"); 197 CallBase* CB = findCall(*F1, "b"); 198 EXPECT_NE(CB, nullptr); 199 200 FunctionPropertiesInfo ExpectedInitial; 201 ExpectedInitial.BasicBlockCount = 1; 202 ExpectedInitial.TotalInstructionCount = 3; 203 ExpectedInitial.Uses = 1; 204 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 205 206 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 207 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 208 209 auto FPI = buildFPI(*F1); 210 EXPECT_EQ(FPI, ExpectedInitial); 211 212 FunctionPropertiesUpdater FPU(FPI, *CB); 213 InlineFunctionInfo IFI; 214 auto IR = llvm::InlineFunction(*CB, IFI); 215 EXPECT_TRUE(IR.isSuccess()); 216 invalidate(*F1); 217 EXPECT_TRUE(FPU.finishAndTest(FAM)); 218 EXPECT_EQ(FPI, ExpectedFinal); 219 } 220 221 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) { 222 LLVMContext C; 223 std::unique_ptr<Module> M = makeLLVMModule(C, 224 R"IR( 225 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 226 target triple = "x86_64-pc-linux-gnu" 227 define i32 @f1(i32 %a) { 228 entry: 229 %i = icmp slt i32 %a, 0 230 br i1 %i, label %if.then, label %if.else 231 if.then: 232 %b = call i32 @f2(i32 %a) 233 %c1 = add i32 %b, 2 234 br label %end 235 if.else: 236 %c2 = add i32 %a, 1 237 br label %end 238 end: 239 %ret = phi i32 [%c1, %if.then],[%c2, %if.else] 240 ret i32 %ret 241 } 242 243 define i32 @f2(i32 %a) { 244 %b = add i32 %a, 1 245 ret i32 %b 246 } 247 )IR"); 248 249 Function *F1 = M->getFunction("f1"); 250 CallBase* CB = findCall(*F1, "b"); 251 EXPECT_NE(CB, nullptr); 252 253 FunctionPropertiesInfo ExpectedInitial; 254 ExpectedInitial.BasicBlockCount = 4; 255 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 256 ExpectedInitial.TotalInstructionCount = 9; 257 ExpectedInitial.Uses = 1; 258 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 259 260 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 261 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 262 263 auto FPI = buildFPI(*F1); 264 EXPECT_EQ(FPI, ExpectedInitial); 265 266 FunctionPropertiesUpdater FPU(FPI, *CB); 267 InlineFunctionInfo IFI; 268 auto IR = llvm::InlineFunction(*CB, IFI); 269 EXPECT_TRUE(IR.isSuccess()); 270 invalidate(*F1); 271 EXPECT_TRUE(FPU.finishAndTest(FAM)); 272 EXPECT_EQ(FPI, ExpectedFinal); 273 } 274 275 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) { 276 LLVMContext C; 277 std::unique_ptr<Module> M = makeLLVMModule(C, 278 R"IR( 279 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 280 target triple = "x86_64-pc-linux-gnu" 281 define i32 @f1(i32 %a) { 282 entry: 283 %i = icmp slt i32 %a, 0 284 br i1 %i, label %if.then, label %if.else 285 if.then: 286 %b = call i32 @f2(i32 %a) 287 %c1 = add i32 %b, 2 288 br label %end 289 if.else: 290 %c2 = add i32 %a, 1 291 br label %end 292 end: 293 %ret = phi i32 [%c1, %if.then],[%c2, %if.else] 294 ret i32 %ret 295 } 296 297 define i32 @f2(i32 %a) { 298 entry: 299 br label %loop 300 loop: 301 %indvar = phi i32 [%indvar.next, %loop], [0, %entry] 302 %b = add i32 %a, %indvar 303 %indvar.next = add i32 %indvar, 1 304 %cond = icmp slt i32 %indvar.next, %a 305 br i1 %cond, label %loop, label %exit 306 exit: 307 ret i32 %b 308 } 309 )IR"); 310 311 Function *F1 = M->getFunction("f1"); 312 CallBase* CB = findCall(*F1, "b"); 313 EXPECT_NE(CB, nullptr); 314 315 FunctionPropertiesInfo ExpectedInitial; 316 ExpectedInitial.BasicBlockCount = 4; 317 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 318 ExpectedInitial.TotalInstructionCount = 9; 319 ExpectedInitial.Uses = 1; 320 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 321 322 FunctionPropertiesInfo ExpectedFinal; 323 ExpectedFinal.BasicBlockCount = 6; 324 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; 325 ExpectedFinal.Uses = 1; 326 ExpectedFinal.MaxLoopDepth = 1; 327 ExpectedFinal.TopLevelLoopCount = 1; 328 ExpectedFinal.TotalInstructionCount = 14; 329 330 auto FPI = buildFPI(*F1); 331 EXPECT_EQ(FPI, ExpectedInitial); 332 FunctionPropertiesUpdater FPU(FPI, *CB); 333 InlineFunctionInfo IFI; 334 335 auto IR = llvm::InlineFunction(*CB, IFI); 336 EXPECT_TRUE(IR.isSuccess()); 337 invalidate(*F1); 338 EXPECT_TRUE(FPU.finishAndTest(FAM)); 339 EXPECT_EQ(FPI, ExpectedFinal); 340 } 341 342 TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) { 343 LLVMContext C; 344 std::unique_ptr<Module> M = makeLLVMModule(C, 345 R"IR( 346 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 347 target triple = "x86_64-pc-linux-gnu" 348 declare void @might_throw() 349 350 define internal void @callee() { 351 entry: 352 call void @might_throw() 353 ret void 354 } 355 356 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 357 entry: 358 invoke void @callee() 359 to label %cont unwind label %exc 360 361 cont: 362 ret i32 0 363 364 exc: 365 %exn = landingpad {i8*, i32} 366 cleanup 367 ret i32 1 368 } 369 370 declare i32 @__gxx_personality_v0(...) 371 )IR"); 372 373 Function *F1 = M->getFunction("caller"); 374 CallBase* CB = findCall(*F1); 375 EXPECT_NE(CB, nullptr); 376 377 auto FPI = buildFPI(*F1); 378 FunctionPropertiesUpdater FPU(FPI, *CB); 379 InlineFunctionInfo IFI; 380 auto IR = llvm::InlineFunction(*CB, IFI); 381 EXPECT_TRUE(IR.isSuccess()); 382 invalidate(*F1); 383 EXPECT_TRUE(FPU.finishAndTest(FAM)); 384 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size()); 385 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 386 F1->getInstructionCount()); 387 } 388 389 TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) { 390 LLVMContext C; 391 std::unique_ptr<Module> M = makeLLVMModule(C, 392 R"IR( 393 declare void @might_throw() 394 395 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { 396 entry: 397 invoke void @might_throw() 398 to label %cont unwind label %exc 399 400 cont: 401 ret i32 0 402 403 exc: 404 %exn = landingpad {i8*, i32} 405 cleanup 406 resume { i8*, i32 } %exn 407 } 408 409 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 410 entry: 411 %X = invoke i32 @callee() 412 to label %cont unwind label %Handler 413 414 cont: 415 ret i32 %X 416 417 Handler: 418 %exn = landingpad {i8*, i32} 419 cleanup 420 ret i32 1 421 } 422 423 declare i32 @__gxx_personality_v0(...) 424 )IR"); 425 426 Function *F1 = M->getFunction("caller"); 427 CallBase* CB = findCall(*F1); 428 EXPECT_NE(CB, nullptr); 429 430 auto FPI = buildFPI(*F1); 431 FunctionPropertiesUpdater FPU(FPI, *CB); 432 InlineFunctionInfo IFI; 433 auto IR = llvm::InlineFunction(*CB, IFI); 434 EXPECT_TRUE(IR.isSuccess()); 435 invalidate(*F1); 436 EXPECT_TRUE(FPU.finishAndTest(FAM)); 437 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 438 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 439 F1->getInstructionCount() - 2); 440 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 441 } 442 443 TEST_F(FunctionPropertiesAnalysisTest, Rethrow) { 444 LLVMContext C; 445 std::unique_ptr<Module> M = makeLLVMModule(C, 446 R"IR( 447 declare void @might_throw() 448 449 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { 450 entry: 451 invoke void @might_throw() 452 to label %cont unwind label %exc 453 454 cont: 455 ret i32 0 456 457 exc: 458 %exn = landingpad {i8*, i32} 459 cleanup 460 resume { i8*, i32 } %exn 461 } 462 463 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 464 entry: 465 %X = invoke i32 @callee() 466 to label %cont unwind label %Handler 467 468 cont: 469 ret i32 %X 470 471 Handler: 472 %exn = landingpad {i8*, i32} 473 cleanup 474 ret i32 1 475 } 476 477 declare i32 @__gxx_personality_v0(...) 478 )IR"); 479 480 Function *F1 = M->getFunction("caller"); 481 CallBase* CB = findCall(*F1); 482 EXPECT_NE(CB, nullptr); 483 484 auto FPI = buildFPI(*F1); 485 FunctionPropertiesUpdater FPU(FPI, *CB); 486 InlineFunctionInfo IFI; 487 auto IR = llvm::InlineFunction(*CB, IFI); 488 EXPECT_TRUE(IR.isSuccess()); 489 invalidate(*F1); 490 EXPECT_TRUE(FPU.finishAndTest(FAM)); 491 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 492 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 493 F1->getInstructionCount() - 2); 494 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 495 } 496 497 TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) { 498 LLVMContext C; 499 std::unique_ptr<Module> M = makeLLVMModule(C, 500 R"IR( 501 declare void @external_func() 502 503 @exception_type1 = external global i8 504 @exception_type2 = external global i8 505 506 507 define internal void @inner() personality i8* null { 508 invoke void @external_func() 509 to label %cont unwind label %lpad 510 cont: 511 ret void 512 lpad: 513 %lp = landingpad i32 514 catch i8* @exception_type1 515 resume i32 %lp 516 } 517 518 define void @outer() personality i8* null { 519 invoke void @inner() 520 to label %cont unwind label %lpad 521 cont: 522 ret void 523 lpad: 524 %lp = landingpad i32 525 cleanup 526 catch i8* @exception_type2 527 resume i32 %lp 528 } 529 530 )IR"); 531 532 Function *F1 = M->getFunction("outer"); 533 CallBase* CB = findCall(*F1); 534 EXPECT_NE(CB, nullptr); 535 536 auto FPI = buildFPI(*F1); 537 FunctionPropertiesUpdater FPU(FPI, *CB); 538 InlineFunctionInfo IFI; 539 auto IR = llvm::InlineFunction(*CB, IFI); 540 EXPECT_TRUE(IR.isSuccess()); 541 invalidate(*F1); 542 EXPECT_TRUE(FPU.finishAndTest(FAM)); 543 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 544 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 545 F1->getInstructionCount() - 2); 546 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 547 } 548 549 TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) { 550 LLVMContext C; 551 std::unique_ptr<Module> M = makeLLVMModule(C, 552 R"IR( 553 declare void @external_func() 554 555 @exception_type1 = external global i8 556 @exception_type2 = external global i8 557 558 559 define internal void @inner() personality i8* null { 560 invoke void @external_func() 561 to label %cont unwind label %lpad 562 cont: 563 ret void 564 lpad: 565 %lp = landingpad i32 566 catch i8* @exception_type1 567 resume i32 %lp 568 } 569 570 define void @outer(i32 %a) personality i8* null { 571 entry: 572 %i = icmp slt i32 %a, 0 573 br i1 %i, label %if.then, label %cont 574 if.then: 575 invoke void @inner() 576 to label %cont unwind label %lpad 577 cont: 578 ret void 579 lpad: 580 %lp = landingpad i32 581 cleanup 582 catch i8* @exception_type2 583 resume i32 %lp 584 } 585 586 )IR"); 587 588 Function *F1 = M->getFunction("outer"); 589 CallBase* CB = findCall(*F1); 590 EXPECT_NE(CB, nullptr); 591 592 auto FPI = buildFPI(*F1); 593 FunctionPropertiesUpdater FPU(FPI, *CB); 594 InlineFunctionInfo IFI; 595 auto IR = llvm::InlineFunction(*CB, IFI); 596 EXPECT_TRUE(IR.isSuccess()); 597 invalidate(*F1); 598 EXPECT_TRUE(FPU.finishAndTest(FAM)); 599 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 600 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 601 F1->getInstructionCount() - 2); 602 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 603 } 604 605 TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) { 606 LLVMContext C; 607 std::unique_ptr<Module> M = makeLLVMModule(C, 608 R"IR( 609 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 610 target triple = "x86_64-pc-linux-gnu" 611 612 declare i32 @a() 613 declare i32 @b() 614 615 define i32 @f1(i32 %a) { 616 entry: 617 br label %loop 618 loop: 619 %i = call i32 @f2(i32 %a) 620 %c = icmp slt i32 %i, %a 621 br i1 %c, label %loop, label %end 622 end: 623 %r = phi i32 [%i, %loop], [%a, %entry] 624 ret i32 %r 625 } 626 627 define i32 @f2(i32 %a) { 628 %cnd = icmp slt i32 %a, 0 629 br i1 %cnd, label %then, label %else 630 then: 631 %r1 = call i32 @a() 632 br label %end 633 else: 634 %r2 = call i32 @b() 635 br label %end 636 end: 637 %r = phi i32 [%r1, %then], [%r2, %else] 638 ret i32 %r 639 } 640 )IR"); 641 642 Function *F1 = M->getFunction("f1"); 643 CallBase *CB = findCall(*F1); 644 EXPECT_NE(CB, nullptr); 645 646 FunctionPropertiesInfo ExpectedInitial; 647 ExpectedInitial.BasicBlockCount = 3; 648 ExpectedInitial.TotalInstructionCount = 6; 649 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 650 ExpectedInitial.Uses = 1; 651 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 652 ExpectedInitial.MaxLoopDepth = 1; 653 ExpectedInitial.TopLevelLoopCount = 1; 654 655 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 656 ExpectedFinal.BasicBlockCount = 6; 657 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 658 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; 659 ExpectedFinal.TotalInstructionCount = 12; 660 661 auto FPI = buildFPI(*F1); 662 EXPECT_EQ(FPI, ExpectedInitial); 663 664 FunctionPropertiesUpdater FPU(FPI, *CB); 665 InlineFunctionInfo IFI; 666 auto IR = llvm::InlineFunction(*CB, IFI); 667 EXPECT_TRUE(IR.isSuccess()); 668 invalidate(*F1); 669 EXPECT_TRUE(FPU.finishAndTest(FAM)); 670 EXPECT_EQ(FPI, ExpectedFinal); 671 } 672 673 TEST_F(FunctionPropertiesAnalysisTest, Unreachable) { 674 LLVMContext C; 675 std::unique_ptr<Module> M = makeLLVMModule(C, 676 R"IR( 677 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 678 target triple = "x86_64-pc-linux-gnu" 679 680 define i64 @f1(i32 noundef %value) { 681 entry: 682 br i1 true, label %cond.true, label %cond.false 683 684 cond.true: ; preds = %entry 685 %conv2 = sext i32 %value to i64 686 br label %cond.end 687 688 cond.false: ; preds = %entry 689 %call3 = call noundef i64 @f2() 690 br label %extra 691 692 extra: 693 br label %extra2 694 695 extra2: 696 br label %cond.end 697 698 cond.end: ; preds = %cond.false, %cond.true 699 %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ] 700 ret i64 %cond 701 } 702 703 define i64 @f2() { 704 entry: 705 tail call void @llvm.trap() 706 unreachable 707 } 708 709 declare void @llvm.trap() 710 )IR"); 711 712 Function *F1 = M->getFunction("f1"); 713 CallBase *CB = findCall(*F1); 714 EXPECT_NE(CB, nullptr); 715 716 FunctionPropertiesInfo ExpectedInitial; 717 ExpectedInitial.BasicBlockCount = 6; 718 ExpectedInitial.TotalInstructionCount = 9; 719 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 720 ExpectedInitial.Uses = 1; 721 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 722 723 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 724 ExpectedFinal.BasicBlockCount = 4; 725 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 726 ExpectedFinal.TotalInstructionCount = 7; 727 728 auto FPI = buildFPI(*F1); 729 EXPECT_EQ(FPI, ExpectedInitial); 730 731 FunctionPropertiesUpdater FPU(FPI, *CB); 732 InlineFunctionInfo IFI; 733 auto IR = llvm::InlineFunction(*CB, IFI); 734 EXPECT_TRUE(IR.isSuccess()); 735 invalidate(*F1); 736 EXPECT_TRUE(FPU.finishAndTest(FAM)); 737 EXPECT_EQ(FPI, ExpectedFinal); 738 } 739 740 TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) { 741 LLVMContext C; 742 std::unique_ptr<Module> M = makeLLVMModule(C, 743 R"IR( 744 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 745 target triple = "x86_64-pc-linux-gnu" 746 747 define i64 @f1(i32 noundef %value) { 748 entry: 749 invoke fastcc void @f2() to label %cont unwind label %lpad 750 cont: 751 ret i64 1 752 lpad: 753 %lp = landingpad i32 cleanup 754 br label %ehcleanup 755 ehcleanup: 756 resume i32 0 757 } 758 define void @f2() { 759 invoke noundef void @f3() to label %exit unwind label %lpad 760 exit: 761 ret void 762 lpad: 763 %lp = landingpad i32 cleanup 764 resume i32 %lp 765 } 766 declare void @f3() 767 )IR"); 768 769 // The outcome of inlining will be that lpad becomes unreachable. The landing 770 // pad of the invoke inherited from f2 will land on a new bb which will branch 771 // to a bb containing the body of lpad. 772 Function *F1 = M->getFunction("f1"); 773 CallBase *CB = findCall(*F1); 774 EXPECT_NE(CB, nullptr); 775 776 FunctionPropertiesInfo ExpectedInitial; 777 ExpectedInitial.BasicBlockCount = 4; 778 ExpectedInitial.TotalInstructionCount = 5; 779 ExpectedInitial.BlocksReachedFromConditionalInstruction = 0; 780 ExpectedInitial.Uses = 1; 781 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 782 783 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 784 ExpectedFinal.BasicBlockCount = 6; 785 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 786 ExpectedFinal.TotalInstructionCount = 8; 787 788 auto FPI = buildFPI(*F1); 789 EXPECT_EQ(FPI, ExpectedInitial); 790 791 FunctionPropertiesUpdater FPU(FPI, *CB); 792 InlineFunctionInfo IFI; 793 auto IR = llvm::InlineFunction(*CB, IFI); 794 EXPECT_TRUE(IR.isSuccess()); 795 invalidate(*F1); 796 EXPECT_TRUE(FPU.finishAndTest(FAM)); 797 EXPECT_EQ(FPI, ExpectedFinal); 798 } 799 } // end anonymous namespace 800