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