xref: /llvm-project/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp (revision fe6bb84c7ec815fab279cc20b10cbe3a3d3ab553)
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