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.ConstantIntOperandCount, 1); 141 EXPECT_EQ(DetailedBranchesFeatures.ConstantFPOperandCount, 0); 142 EXPECT_EQ(DetailedBranchesFeatures.ConstantOperandCount, 0); 143 EXPECT_EQ(DetailedBranchesFeatures.InstructionOperandCount, 4); 144 EXPECT_EQ(DetailedBranchesFeatures.BasicBlockOperandCount, 4); 145 EXPECT_EQ(DetailedBranchesFeatures.GlobalValueOperandCount, 2); 146 EXPECT_EQ(DetailedBranchesFeatures.InlineAsmOperandCount, 0); 147 EXPECT_EQ(DetailedBranchesFeatures.ArgumentOperandCount, 3); 148 EXPECT_EQ(DetailedBranchesFeatures.UnknownOperandCount, 0); 149 EnableDetailedFunctionProperties.setValue(false); 150 } 151 152 TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) { 153 LLVMContext C; 154 std::unique_ptr<Module> M = makeLLVMModule(C, 155 R"IR( 156 define i64 @f1() { 157 br i1 0, label %br1, label %finally 158 br1: 159 ret i64 0 160 finally: 161 ret i64 3 162 } 163 )IR"); 164 165 Function *F1 = M->getFunction("f1"); 166 EnableDetailedFunctionProperties.setValue(true); 167 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); 168 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0); 169 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 1); 170 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0); 171 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 2); 172 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0); 173 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0); 174 EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0); 175 EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0); 176 EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 3); 177 EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0); 178 EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0); 179 EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 0); 180 EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 3); 181 EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0); 182 EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0); 183 EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 0); 184 EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 2); 185 EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 0); 186 EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 0); 187 EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 0); 188 EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0); 189 EnableDetailedFunctionProperties.setValue(false); 190 } 191 192 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) { 193 LLVMContext C; 194 std::unique_ptr<Module> M = makeLLVMModule(C, 195 R"IR( 196 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 197 target triple = "x86_64-pc-linux-gnu" 198 define i32 @f1(i32 %a) { 199 %b = call i32 @f2(i32 %a) 200 %c = add i32 %b, 2 201 ret i32 %c 202 } 203 204 define i32 @f2(i32 %a) { 205 %b = add i32 %a, 1 206 ret i32 %b 207 } 208 )IR"); 209 210 Function *F1 = M->getFunction("f1"); 211 CallBase* CB = findCall(*F1, "b"); 212 EXPECT_NE(CB, nullptr); 213 214 FunctionPropertiesInfo ExpectedInitial; 215 ExpectedInitial.BasicBlockCount = 1; 216 ExpectedInitial.TotalInstructionCount = 3; 217 ExpectedInitial.Uses = 1; 218 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 219 220 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 221 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 222 223 auto FPI = buildFPI(*F1); 224 EXPECT_EQ(FPI, ExpectedInitial); 225 226 FunctionPropertiesUpdater FPU(FPI, *CB); 227 InlineFunctionInfo IFI; 228 auto IR = llvm::InlineFunction(*CB, IFI); 229 EXPECT_TRUE(IR.isSuccess()); 230 invalidate(*F1); 231 EXPECT_TRUE(FPU.finishAndTest(FAM)); 232 EXPECT_EQ(FPI, ExpectedFinal); 233 } 234 235 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) { 236 LLVMContext C; 237 std::unique_ptr<Module> M = makeLLVMModule(C, 238 R"IR( 239 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 240 target triple = "x86_64-pc-linux-gnu" 241 define i32 @f1(i32 %a) { 242 entry: 243 %i = icmp slt i32 %a, 0 244 br i1 %i, label %if.then, label %if.else 245 if.then: 246 %b = call i32 @f2(i32 %a) 247 %c1 = add i32 %b, 2 248 br label %end 249 if.else: 250 %c2 = add i32 %a, 1 251 br label %end 252 end: 253 %ret = phi i32 [%c1, %if.then],[%c2, %if.else] 254 ret i32 %ret 255 } 256 257 define i32 @f2(i32 %a) { 258 %b = add i32 %a, 1 259 ret i32 %b 260 } 261 )IR"); 262 263 Function *F1 = M->getFunction("f1"); 264 CallBase* CB = findCall(*F1, "b"); 265 EXPECT_NE(CB, nullptr); 266 267 FunctionPropertiesInfo ExpectedInitial; 268 ExpectedInitial.BasicBlockCount = 4; 269 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 270 ExpectedInitial.TotalInstructionCount = 9; 271 ExpectedInitial.Uses = 1; 272 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 273 274 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 275 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 276 277 auto FPI = buildFPI(*F1); 278 EXPECT_EQ(FPI, ExpectedInitial); 279 280 FunctionPropertiesUpdater FPU(FPI, *CB); 281 InlineFunctionInfo IFI; 282 auto IR = llvm::InlineFunction(*CB, IFI); 283 EXPECT_TRUE(IR.isSuccess()); 284 invalidate(*F1); 285 EXPECT_TRUE(FPU.finishAndTest(FAM)); 286 EXPECT_EQ(FPI, ExpectedFinal); 287 } 288 289 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) { 290 LLVMContext C; 291 std::unique_ptr<Module> M = makeLLVMModule(C, 292 R"IR( 293 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 294 target triple = "x86_64-pc-linux-gnu" 295 define i32 @f1(i32 %a) { 296 entry: 297 %i = icmp slt i32 %a, 0 298 br i1 %i, label %if.then, label %if.else 299 if.then: 300 %b = call i32 @f2(i32 %a) 301 %c1 = add i32 %b, 2 302 br label %end 303 if.else: 304 %c2 = add i32 %a, 1 305 br label %end 306 end: 307 %ret = phi i32 [%c1, %if.then],[%c2, %if.else] 308 ret i32 %ret 309 } 310 311 define i32 @f2(i32 %a) { 312 entry: 313 br label %loop 314 loop: 315 %indvar = phi i32 [%indvar.next, %loop], [0, %entry] 316 %b = add i32 %a, %indvar 317 %indvar.next = add i32 %indvar, 1 318 %cond = icmp slt i32 %indvar.next, %a 319 br i1 %cond, label %loop, label %exit 320 exit: 321 ret i32 %b 322 } 323 )IR"); 324 325 Function *F1 = M->getFunction("f1"); 326 CallBase* CB = findCall(*F1, "b"); 327 EXPECT_NE(CB, nullptr); 328 329 FunctionPropertiesInfo ExpectedInitial; 330 ExpectedInitial.BasicBlockCount = 4; 331 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 332 ExpectedInitial.TotalInstructionCount = 9; 333 ExpectedInitial.Uses = 1; 334 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 335 336 FunctionPropertiesInfo ExpectedFinal; 337 ExpectedFinal.BasicBlockCount = 6; 338 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; 339 ExpectedFinal.Uses = 1; 340 ExpectedFinal.MaxLoopDepth = 1; 341 ExpectedFinal.TopLevelLoopCount = 1; 342 ExpectedFinal.TotalInstructionCount = 14; 343 344 auto FPI = buildFPI(*F1); 345 EXPECT_EQ(FPI, ExpectedInitial); 346 FunctionPropertiesUpdater FPU(FPI, *CB); 347 InlineFunctionInfo IFI; 348 349 auto IR = llvm::InlineFunction(*CB, IFI); 350 EXPECT_TRUE(IR.isSuccess()); 351 invalidate(*F1); 352 EXPECT_TRUE(FPU.finishAndTest(FAM)); 353 EXPECT_EQ(FPI, ExpectedFinal); 354 } 355 356 TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) { 357 LLVMContext C; 358 std::unique_ptr<Module> M = makeLLVMModule(C, 359 R"IR( 360 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 361 target triple = "x86_64-pc-linux-gnu" 362 declare void @might_throw() 363 364 define internal void @callee() { 365 entry: 366 call void @might_throw() 367 ret void 368 } 369 370 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 371 entry: 372 invoke void @callee() 373 to label %cont unwind label %exc 374 375 cont: 376 ret i32 0 377 378 exc: 379 %exn = landingpad {i8*, i32} 380 cleanup 381 ret i32 1 382 } 383 384 declare i32 @__gxx_personality_v0(...) 385 )IR"); 386 387 Function *F1 = M->getFunction("caller"); 388 CallBase* CB = findCall(*F1); 389 EXPECT_NE(CB, nullptr); 390 391 auto FPI = buildFPI(*F1); 392 FunctionPropertiesUpdater FPU(FPI, *CB); 393 InlineFunctionInfo IFI; 394 auto IR = llvm::InlineFunction(*CB, IFI); 395 EXPECT_TRUE(IR.isSuccess()); 396 invalidate(*F1); 397 EXPECT_TRUE(FPU.finishAndTest(FAM)); 398 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size()); 399 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 400 F1->getInstructionCount()); 401 } 402 403 TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) { 404 LLVMContext C; 405 std::unique_ptr<Module> M = makeLLVMModule(C, 406 R"IR( 407 declare void @might_throw() 408 409 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { 410 entry: 411 invoke void @might_throw() 412 to label %cont unwind label %exc 413 414 cont: 415 ret i32 0 416 417 exc: 418 %exn = landingpad {i8*, i32} 419 cleanup 420 resume { i8*, i32 } %exn 421 } 422 423 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 424 entry: 425 %X = invoke i32 @callee() 426 to label %cont unwind label %Handler 427 428 cont: 429 ret i32 %X 430 431 Handler: 432 %exn = landingpad {i8*, i32} 433 cleanup 434 ret i32 1 435 } 436 437 declare i32 @__gxx_personality_v0(...) 438 )IR"); 439 440 Function *F1 = M->getFunction("caller"); 441 CallBase* CB = findCall(*F1); 442 EXPECT_NE(CB, nullptr); 443 444 auto FPI = buildFPI(*F1); 445 FunctionPropertiesUpdater FPU(FPI, *CB); 446 InlineFunctionInfo IFI; 447 auto IR = llvm::InlineFunction(*CB, IFI); 448 EXPECT_TRUE(IR.isSuccess()); 449 invalidate(*F1); 450 EXPECT_TRUE(FPU.finishAndTest(FAM)); 451 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 452 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 453 F1->getInstructionCount() - 2); 454 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 455 } 456 457 TEST_F(FunctionPropertiesAnalysisTest, Rethrow) { 458 LLVMContext C; 459 std::unique_ptr<Module> M = makeLLVMModule(C, 460 R"IR( 461 declare void @might_throw() 462 463 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { 464 entry: 465 invoke void @might_throw() 466 to label %cont unwind label %exc 467 468 cont: 469 ret i32 0 470 471 exc: 472 %exn = landingpad {i8*, i32} 473 cleanup 474 resume { i8*, i32 } %exn 475 } 476 477 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { 478 entry: 479 %X = invoke i32 @callee() 480 to label %cont unwind label %Handler 481 482 cont: 483 ret i32 %X 484 485 Handler: 486 %exn = landingpad {i8*, i32} 487 cleanup 488 ret i32 1 489 } 490 491 declare i32 @__gxx_personality_v0(...) 492 )IR"); 493 494 Function *F1 = M->getFunction("caller"); 495 CallBase* CB = findCall(*F1); 496 EXPECT_NE(CB, nullptr); 497 498 auto FPI = buildFPI(*F1); 499 FunctionPropertiesUpdater FPU(FPI, *CB); 500 InlineFunctionInfo IFI; 501 auto IR = llvm::InlineFunction(*CB, IFI); 502 EXPECT_TRUE(IR.isSuccess()); 503 invalidate(*F1); 504 EXPECT_TRUE(FPU.finishAndTest(FAM)); 505 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 506 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 507 F1->getInstructionCount() - 2); 508 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 509 } 510 511 TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) { 512 LLVMContext C; 513 std::unique_ptr<Module> M = makeLLVMModule(C, 514 R"IR( 515 declare void @external_func() 516 517 @exception_type1 = external global i8 518 @exception_type2 = external global i8 519 520 521 define internal void @inner() personality i8* null { 522 invoke void @external_func() 523 to label %cont unwind label %lpad 524 cont: 525 ret void 526 lpad: 527 %lp = landingpad i32 528 catch i8* @exception_type1 529 resume i32 %lp 530 } 531 532 define void @outer() personality i8* null { 533 invoke void @inner() 534 to label %cont unwind label %lpad 535 cont: 536 ret void 537 lpad: 538 %lp = landingpad i32 539 cleanup 540 catch i8* @exception_type2 541 resume i32 %lp 542 } 543 544 )IR"); 545 546 Function *F1 = M->getFunction("outer"); 547 CallBase* CB = findCall(*F1); 548 EXPECT_NE(CB, nullptr); 549 550 auto FPI = buildFPI(*F1); 551 FunctionPropertiesUpdater FPU(FPI, *CB); 552 InlineFunctionInfo IFI; 553 auto IR = llvm::InlineFunction(*CB, IFI); 554 EXPECT_TRUE(IR.isSuccess()); 555 invalidate(*F1); 556 EXPECT_TRUE(FPU.finishAndTest(FAM)); 557 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 558 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 559 F1->getInstructionCount() - 2); 560 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 561 } 562 563 TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) { 564 LLVMContext C; 565 std::unique_ptr<Module> M = makeLLVMModule(C, 566 R"IR( 567 declare void @external_func() 568 569 @exception_type1 = external global i8 570 @exception_type2 = external global i8 571 572 573 define internal void @inner() personality i8* null { 574 invoke void @external_func() 575 to label %cont unwind label %lpad 576 cont: 577 ret void 578 lpad: 579 %lp = landingpad i32 580 catch i8* @exception_type1 581 resume i32 %lp 582 } 583 584 define void @outer(i32 %a) personality i8* null { 585 entry: 586 %i = icmp slt i32 %a, 0 587 br i1 %i, label %if.then, label %cont 588 if.then: 589 invoke void @inner() 590 to label %cont unwind label %lpad 591 cont: 592 ret void 593 lpad: 594 %lp = landingpad i32 595 cleanup 596 catch i8* @exception_type2 597 resume i32 %lp 598 } 599 600 )IR"); 601 602 Function *F1 = M->getFunction("outer"); 603 CallBase* CB = findCall(*F1); 604 EXPECT_NE(CB, nullptr); 605 606 auto FPI = buildFPI(*F1); 607 FunctionPropertiesUpdater FPU(FPI, *CB); 608 InlineFunctionInfo IFI; 609 auto IR = llvm::InlineFunction(*CB, IFI); 610 EXPECT_TRUE(IR.isSuccess()); 611 invalidate(*F1); 612 EXPECT_TRUE(FPU.finishAndTest(FAM)); 613 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); 614 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), 615 F1->getInstructionCount() - 2); 616 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); 617 } 618 619 TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) { 620 LLVMContext C; 621 std::unique_ptr<Module> M = makeLLVMModule(C, 622 R"IR( 623 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 624 target triple = "x86_64-pc-linux-gnu" 625 626 declare i32 @a() 627 declare i32 @b() 628 629 define i32 @f1(i32 %a) { 630 entry: 631 br label %loop 632 loop: 633 %i = call i32 @f2(i32 %a) 634 %c = icmp slt i32 %i, %a 635 br i1 %c, label %loop, label %end 636 end: 637 %r = phi i32 [%i, %loop], [%a, %entry] 638 ret i32 %r 639 } 640 641 define i32 @f2(i32 %a) { 642 %cnd = icmp slt i32 %a, 0 643 br i1 %cnd, label %then, label %else 644 then: 645 %r1 = call i32 @a() 646 br label %end 647 else: 648 %r2 = call i32 @b() 649 br label %end 650 end: 651 %r = phi i32 [%r1, %then], [%r2, %else] 652 ret i32 %r 653 } 654 )IR"); 655 656 Function *F1 = M->getFunction("f1"); 657 CallBase *CB = findCall(*F1); 658 EXPECT_NE(CB, nullptr); 659 660 FunctionPropertiesInfo ExpectedInitial; 661 ExpectedInitial.BasicBlockCount = 3; 662 ExpectedInitial.TotalInstructionCount = 6; 663 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 664 ExpectedInitial.Uses = 1; 665 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 666 ExpectedInitial.MaxLoopDepth = 1; 667 ExpectedInitial.TopLevelLoopCount = 1; 668 669 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 670 ExpectedFinal.BasicBlockCount = 6; 671 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 672 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; 673 ExpectedFinal.TotalInstructionCount = 12; 674 675 auto FPI = buildFPI(*F1); 676 EXPECT_EQ(FPI, ExpectedInitial); 677 678 FunctionPropertiesUpdater FPU(FPI, *CB); 679 InlineFunctionInfo IFI; 680 auto IR = llvm::InlineFunction(*CB, IFI); 681 EXPECT_TRUE(IR.isSuccess()); 682 invalidate(*F1); 683 EXPECT_TRUE(FPU.finishAndTest(FAM)); 684 EXPECT_EQ(FPI, ExpectedFinal); 685 } 686 687 TEST_F(FunctionPropertiesAnalysisTest, Unreachable) { 688 LLVMContext C; 689 std::unique_ptr<Module> M = makeLLVMModule(C, 690 R"IR( 691 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 692 target triple = "x86_64-pc-linux-gnu" 693 694 define i64 @f1(i32 noundef %value) { 695 entry: 696 br i1 true, label %cond.true, label %cond.false 697 698 cond.true: ; preds = %entry 699 %conv2 = sext i32 %value to i64 700 br label %cond.end 701 702 cond.false: ; preds = %entry 703 %call3 = call noundef i64 @f2() 704 br label %extra 705 706 extra: 707 br label %extra2 708 709 extra2: 710 br label %cond.end 711 712 cond.end: ; preds = %cond.false, %cond.true 713 %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ] 714 ret i64 %cond 715 } 716 717 define i64 @f2() { 718 entry: 719 tail call void @llvm.trap() 720 unreachable 721 } 722 723 declare void @llvm.trap() 724 )IR"); 725 726 Function *F1 = M->getFunction("f1"); 727 CallBase *CB = findCall(*F1); 728 EXPECT_NE(CB, nullptr); 729 730 FunctionPropertiesInfo ExpectedInitial; 731 ExpectedInitial.BasicBlockCount = 6; 732 ExpectedInitial.TotalInstructionCount = 9; 733 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; 734 ExpectedInitial.Uses = 1; 735 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 736 737 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 738 ExpectedFinal.BasicBlockCount = 4; 739 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 740 ExpectedFinal.TotalInstructionCount = 7; 741 742 auto FPI = buildFPI(*F1); 743 EXPECT_EQ(FPI, ExpectedInitial); 744 745 FunctionPropertiesUpdater FPU(FPI, *CB); 746 InlineFunctionInfo IFI; 747 auto IR = llvm::InlineFunction(*CB, IFI); 748 EXPECT_TRUE(IR.isSuccess()); 749 invalidate(*F1); 750 EXPECT_TRUE(FPU.finishAndTest(FAM)); 751 EXPECT_EQ(FPI, ExpectedFinal); 752 } 753 754 TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) { 755 LLVMContext C; 756 std::unique_ptr<Module> M = makeLLVMModule(C, 757 R"IR( 758 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 759 target triple = "x86_64-pc-linux-gnu" 760 761 define i64 @f1(i32 noundef %value) { 762 entry: 763 invoke fastcc void @f2() to label %cont unwind label %lpad 764 cont: 765 ret i64 1 766 lpad: 767 %lp = landingpad i32 cleanup 768 br label %ehcleanup 769 ehcleanup: 770 resume i32 0 771 } 772 define void @f2() { 773 invoke noundef void @f3() to label %exit unwind label %lpad 774 exit: 775 ret void 776 lpad: 777 %lp = landingpad i32 cleanup 778 resume i32 %lp 779 } 780 declare void @f3() 781 )IR"); 782 783 // The outcome of inlining will be that lpad becomes unreachable. The landing 784 // pad of the invoke inherited from f2 will land on a new bb which will branch 785 // to a bb containing the body of lpad. 786 Function *F1 = M->getFunction("f1"); 787 CallBase *CB = findCall(*F1); 788 EXPECT_NE(CB, nullptr); 789 790 FunctionPropertiesInfo ExpectedInitial; 791 ExpectedInitial.BasicBlockCount = 4; 792 ExpectedInitial.TotalInstructionCount = 5; 793 ExpectedInitial.BlocksReachedFromConditionalInstruction = 0; 794 ExpectedInitial.Uses = 1; 795 ExpectedInitial.DirectCallsToDefinedFunctions = 1; 796 797 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; 798 ExpectedFinal.BasicBlockCount = 6; 799 ExpectedFinal.DirectCallsToDefinedFunctions = 0; 800 ExpectedFinal.TotalInstructionCount = 8; 801 802 auto FPI = buildFPI(*F1); 803 EXPECT_EQ(FPI, ExpectedInitial); 804 805 FunctionPropertiesUpdater FPU(FPI, *CB); 806 InlineFunctionInfo IFI; 807 auto IR = llvm::InlineFunction(*CB, IFI); 808 EXPECT_TRUE(IR.isSuccess()); 809 invalidate(*F1); 810 EXPECT_TRUE(FPU.finishAndTest(FAM)); 811 EXPECT_EQ(FPI, ExpectedFinal); 812 } 813 814 TEST_F(FunctionPropertiesAnalysisTest, DetailedOperandCount) { 815 LLVMContext C; 816 std::unique_ptr<Module> M = makeLLVMModule(C, 817 R"IR( 818 @a = global i64 1 819 820 define i64 @f1(i64 %e) { 821 %b = load i64, i64* @a 822 %c = add i64 %b, 2 823 %d = call i64 asm "mov $1,$0", "=r,r" (i64 %c) 824 %f = add i64 %d, %e 825 ret i64 %f 826 } 827 )IR"); 828 829 Function *F1 = M->getFunction("f1"); 830 EnableDetailedFunctionProperties.setValue(true); 831 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); 832 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0); 833 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 0); 834 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0); 835 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 0); 836 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0); 837 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0); 838 EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0); 839 EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0); 840 EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 1); 841 EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0); 842 EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0); 843 EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 4); 844 EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 1); 845 EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0); 846 EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0); 847 EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 4); 848 EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 0); 849 EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 1); 850 EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 1); 851 EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 1); 852 EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0); 853 EnableDetailedFunctionProperties.setValue(false); 854 } 855 } // end anonymous namespace 856