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/Analysis/AliasAnalysis.h"
11#include "llvm/Analysis/LoopInfo.h"
12#include "llvm/AsmParser/Parser.h"
13#include "llvm/IR/Dominators.h"
14#include "llvm/IR/Instructions.h"
15#include "llvm/IR/LLVMContext.h"
16#include "llvm/IR/Module.h"
17#include "llvm/IR/PassManager.h"
18#include "llvm/Passes/PassBuilder.h"
19#include "llvm/Passes/StandardInstrumentations.h"
20#include "llvm/Support/SourceMgr.h"
21#include "llvm/Transforms/Utils/Cloning.h"
22#include "gtest/gtest.h"
23#include <cstring>
24
25using namespace llvm;
26
27namespace llvm {
28extern cl::opt<bool> EnableDetailedFunctionProperties;
29extern cl::opt<bool> BigBasicBlockInstructionThreshold;
30extern cl::opt<bool> MediumBasicBlockInstrutionThreshold;
31} // namespace llvm
32
33namespace {
34
35class FunctionPropertiesAnalysisTest : public testing::Test {
36public:
37 FunctionPropertiesAnalysisTest() {
38 FAM.registerPass(PassBuilder: [&] { return DominatorTreeAnalysis(); });
39 FAM.registerPass(PassBuilder: [&] { return LoopAnalysis(); });
40 FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(); });
41 }
42
43protected:
44 std::unique_ptr<DominatorTree> DT;
45 std::unique_ptr<LoopInfo> LI;
46 FunctionAnalysisManager FAM;
47
48 FunctionPropertiesInfo buildFPI(Function &F) {
49 return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM);
50 }
51
52 void invalidate(Function &F) {
53 PreservedAnalyses PA = PreservedAnalyses::none();
54 FAM.invalidate(IR&: F, PA);
55 }
56
57 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
58 SMDiagnostic Err;
59 std::unique_ptr<Module> Mod = parseAssemblyString(AsmString: IR, Err, Context&: C);
60 if (!Mod)
61 Err.print(ProgName: "MLAnalysisTests", S&: errs());
62 return Mod;
63 }
64
65 CallBase* findCall(Function& F, const char* Name = nullptr) {
66 for (auto &BB : F)
67 for (auto &I : BB )
68 if (auto *CB = dyn_cast<CallBase>(Val: &I))
69 if (!Name || CB->getName() == Name)
70 return CB;
71 return nullptr;
72 }
73};
74
75TEST_F(FunctionPropertiesAnalysisTest, BasicTest) {
76 LLVMContext C;
77 std::unique_ptr<Module> M = makeLLVMModule(C,
78 IR: R"IR(
79target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
80target triple = "x86_64-pc-linux-gnu"
81declare i32 @f1(i32)
82declare i32 @f2(i32)
83define i32 @branches(i32) {
84 %cond = icmp slt i32 %0, 3
85 br i1 %cond, label %then, label %else
86then:
87 %ret.1 = call i32 @f1(i32 %0)
88 br label %last.block
89else:
90 %ret.2 = call i32 @f2(i32 %0)
91 br label %last.block
92last.block:
93 %ret = phi i32 [%ret.1, %then], [%ret.2, %else]
94 ret i32 %ret
95}
96define internal i32 @top() {
97 %1 = call i32 @branches(i32 2)
98 %2 = call i32 @f1(i32 %1)
99 ret i32 %2
100}
101)IR");
102
103 Function *BranchesFunction = M->getFunction(Name: "branches");
104 FunctionPropertiesInfo BranchesFeatures = buildFPI(F&: *BranchesFunction);
105 EXPECT_EQ(BranchesFeatures.BasicBlockCount, 4);
106 EXPECT_EQ(BranchesFeatures.BlocksReachedFromConditionalInstruction, 2);
107 // 2 Users: top is one. The other is added because @branches is not internal,
108 // so it may have external callers.
109 EXPECT_EQ(BranchesFeatures.Uses, 2);
110 EXPECT_EQ(BranchesFeatures.DirectCallsToDefinedFunctions, 0);
111 EXPECT_EQ(BranchesFeatures.LoadInstCount, 0);
112 EXPECT_EQ(BranchesFeatures.StoreInstCount, 0);
113 EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0);
114 EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0);
115
116 Function *TopFunction = M->getFunction(Name: "top");
117 FunctionPropertiesInfo TopFeatures = buildFPI(F&: *TopFunction);
118 EXPECT_EQ(TopFeatures.BasicBlockCount, 1);
119 EXPECT_EQ(TopFeatures.BlocksReachedFromConditionalInstruction, 0);
120 EXPECT_EQ(TopFeatures.Uses, 0);
121 EXPECT_EQ(TopFeatures.DirectCallsToDefinedFunctions, 1);
122 EXPECT_EQ(BranchesFeatures.LoadInstCount, 0);
123 EXPECT_EQ(BranchesFeatures.StoreInstCount, 0);
124 EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0);
125 EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0);
126
127 EnableDetailedFunctionProperties.setValue(V: true);
128 FunctionPropertiesInfo DetailedBranchesFeatures = buildFPI(F&: *BranchesFunction);
129 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSingleSuccessor, 2);
130 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoSuccessors, 1);
131 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoSuccessors, 0);
132 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSinglePredecessor, 2);
133 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoPredecessors, 1);
134 EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoPredecessors, 0);
135 EXPECT_EQ(DetailedBranchesFeatures.BigBasicBlocks, 0);
136 EXPECT_EQ(DetailedBranchesFeatures.MediumBasicBlocks, 0);
137 EXPECT_EQ(DetailedBranchesFeatures.SmallBasicBlocks, 4);
138 EXPECT_EQ(DetailedBranchesFeatures.CastInstructionCount, 0);
139 EXPECT_EQ(DetailedBranchesFeatures.FloatingPointInstructionCount, 0);
140 EXPECT_EQ(DetailedBranchesFeatures.IntegerInstructionCount, 4);
141 EXPECT_EQ(DetailedBranchesFeatures.ConstantIntOperandCount, 1);
142 EXPECT_EQ(DetailedBranchesFeatures.ConstantFPOperandCount, 0);
143 EXPECT_EQ(DetailedBranchesFeatures.ConstantOperandCount, 0);
144 EXPECT_EQ(DetailedBranchesFeatures.InstructionOperandCount, 4);
145 EXPECT_EQ(DetailedBranchesFeatures.BasicBlockOperandCount, 4);
146 EXPECT_EQ(DetailedBranchesFeatures.GlobalValueOperandCount, 2);
147 EXPECT_EQ(DetailedBranchesFeatures.InlineAsmOperandCount, 0);
148 EXPECT_EQ(DetailedBranchesFeatures.ArgumentOperandCount, 3);
149 EXPECT_EQ(DetailedBranchesFeatures.UnknownOperandCount, 0);
150 EXPECT_EQ(DetailedBranchesFeatures.CriticalEdgeCount, 0);
151 EXPECT_EQ(DetailedBranchesFeatures.ControlFlowEdgeCount, 4);
152 EXPECT_EQ(DetailedBranchesFeatures.UnconditionalBranchCount, 2);
153 EXPECT_EQ(DetailedBranchesFeatures.IntrinsicCount, 0);
154 EXPECT_EQ(DetailedBranchesFeatures.DirectCallCount, 2);
155 EXPECT_EQ(DetailedBranchesFeatures.IndirectCallCount, 0);
156 EXPECT_EQ(DetailedBranchesFeatures.CallReturnsIntegerCount, 2);
157 EXPECT_EQ(DetailedBranchesFeatures.CallReturnsFloatCount, 0);
158 EXPECT_EQ(DetailedBranchesFeatures.CallReturnsPointerCount, 0);
159 EXPECT_EQ(DetailedBranchesFeatures.CallWithManyArgumentsCount, 0);
160 EXPECT_EQ(DetailedBranchesFeatures.CallWithPointerArgumentCount, 0);
161 EnableDetailedFunctionProperties.setValue(V: false);
162}
163
164TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) {
165 LLVMContext C;
166 std::unique_ptr<Module> M = makeLLVMModule(C,
167 IR: R"IR(
168define i64 @f1() {
169 br i1 0, label %br1, label %finally
170br1:
171 ret i64 0
172finally:
173 ret i64 3
174}
175)IR");
176
177 Function *F1 = M->getFunction(Name: "f1");
178 EnableDetailedFunctionProperties.setValue(V: true);
179 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
180 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0);
181 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 1);
182 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0);
183 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 2);
184 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0);
185 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0);
186 EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0);
187 EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0);
188 EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 3);
189 EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0);
190 EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0);
191 EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 0);
192 EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 3);
193 EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0);
194 EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0);
195 EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 0);
196 EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 2);
197 EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 0);
198 EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 0);
199 EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 0);
200 EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0);
201 EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 0);
202 EXPECT_EQ(DetailedF1Properties.ControlFlowEdgeCount, 2);
203 EXPECT_EQ(DetailedF1Properties.UnconditionalBranchCount, 0);
204 EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0);
205 EXPECT_EQ(DetailedF1Properties.DirectCallCount, 0);
206 EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0);
207 EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 0);
208 EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 0);
209 EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0);
210 EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0);
211 EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0);
212 EnableDetailedFunctionProperties.setValue(V: false);
213}
214
215TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) {
216 LLVMContext C;
217 std::unique_ptr<Module> M = makeLLVMModule(C,
218 IR: R"IR(
219target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
220target triple = "x86_64-pc-linux-gnu"
221define i32 @f1(i32 %a) {
222 %b = call i32 @f2(i32 %a)
223 %c = add i32 %b, 2
224 ret i32 %c
225}
226
227define i32 @f2(i32 %a) {
228 %b = add i32 %a, 1
229 ret i32 %b
230}
231)IR");
232
233 Function *F1 = M->getFunction(Name: "f1");
234 CallBase* CB = findCall(F&: *F1, Name: "b");
235 EXPECT_NE(CB, nullptr);
236
237 FunctionPropertiesInfo ExpectedInitial;
238 ExpectedInitial.BasicBlockCount = 1;
239 ExpectedInitial.TotalInstructionCount = 3;
240 ExpectedInitial.Uses = 1;
241 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
242
243 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
244 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
245
246 auto FPI = buildFPI(F&: *F1);
247 EXPECT_EQ(FPI, ExpectedInitial);
248
249 FunctionPropertiesUpdater FPU(FPI, *CB);
250 InlineFunctionInfo IFI;
251 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
252 EXPECT_TRUE(IR.isSuccess());
253 invalidate(F&: *F1);
254 EXPECT_TRUE(FPU.finishAndTest(FAM));
255 EXPECT_EQ(FPI, ExpectedFinal);
256}
257
258TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) {
259 LLVMContext C;
260 std::unique_ptr<Module> M = makeLLVMModule(C,
261 IR: R"IR(
262target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
263target triple = "x86_64-pc-linux-gnu"
264define i32 @f1(i32 %a) {
265entry:
266 %i = icmp slt i32 %a, 0
267 br i1 %i, label %if.then, label %if.else
268if.then:
269 %b = call i32 @f2(i32 %a)
270 %c1 = add i32 %b, 2
271 br label %end
272if.else:
273 %c2 = add i32 %a, 1
274 br label %end
275end:
276 %ret = phi i32 [%c1, %if.then],[%c2, %if.else]
277 ret i32 %ret
278}
279
280define i32 @f2(i32 %a) {
281 %b = add i32 %a, 1
282 ret i32 %b
283}
284)IR");
285
286 Function *F1 = M->getFunction(Name: "f1");
287 CallBase* CB = findCall(F&: *F1, Name: "b");
288 EXPECT_NE(CB, nullptr);
289
290 FunctionPropertiesInfo ExpectedInitial;
291 ExpectedInitial.BasicBlockCount = 4;
292 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
293 ExpectedInitial.TotalInstructionCount = 9;
294 ExpectedInitial.Uses = 1;
295 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
296
297 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
298 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
299
300 auto FPI = buildFPI(F&: *F1);
301 EXPECT_EQ(FPI, ExpectedInitial);
302
303 FunctionPropertiesUpdater FPU(FPI, *CB);
304 InlineFunctionInfo IFI;
305 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
306 EXPECT_TRUE(IR.isSuccess());
307 invalidate(F&: *F1);
308 EXPECT_TRUE(FPU.finishAndTest(FAM));
309 EXPECT_EQ(FPI, ExpectedFinal);
310}
311
312TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) {
313 LLVMContext C;
314 std::unique_ptr<Module> M = makeLLVMModule(C,
315 IR: R"IR(
316target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
317target triple = "x86_64-pc-linux-gnu"
318define i32 @f1(i32 %a) {
319entry:
320 %i = icmp slt i32 %a, 0
321 br i1 %i, label %if.then, label %if.else
322if.then:
323 %b = call i32 @f2(i32 %a)
324 %c1 = add i32 %b, 2
325 br label %end
326if.else:
327 %c2 = add i32 %a, 1
328 br label %end
329end:
330 %ret = phi i32 [%c1, %if.then],[%c2, %if.else]
331 ret i32 %ret
332}
333
334define i32 @f2(i32 %a) {
335entry:
336 br label %loop
337loop:
338 %indvar = phi i32 [%indvar.next, %loop], [0, %entry]
339 %b = add i32 %a, %indvar
340 %indvar.next = add i32 %indvar, 1
341 %cond = icmp slt i32 %indvar.next, %a
342 br i1 %cond, label %loop, label %exit
343exit:
344 ret i32 %b
345}
346)IR");
347
348 Function *F1 = M->getFunction(Name: "f1");
349 CallBase* CB = findCall(F&: *F1, Name: "b");
350 EXPECT_NE(CB, nullptr);
351
352 FunctionPropertiesInfo ExpectedInitial;
353 ExpectedInitial.BasicBlockCount = 4;
354 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
355 ExpectedInitial.TotalInstructionCount = 9;
356 ExpectedInitial.Uses = 1;
357 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
358
359 FunctionPropertiesInfo ExpectedFinal;
360 ExpectedFinal.BasicBlockCount = 6;
361 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;
362 ExpectedFinal.Uses = 1;
363 ExpectedFinal.MaxLoopDepth = 1;
364 ExpectedFinal.TopLevelLoopCount = 1;
365 ExpectedFinal.TotalInstructionCount = 14;
366
367 auto FPI = buildFPI(F&: *F1);
368 EXPECT_EQ(FPI, ExpectedInitial);
369 FunctionPropertiesUpdater FPU(FPI, *CB);
370 InlineFunctionInfo IFI;
371
372 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
373 EXPECT_TRUE(IR.isSuccess());
374 invalidate(F&: *F1);
375 EXPECT_TRUE(FPU.finishAndTest(FAM));
376 EXPECT_EQ(FPI, ExpectedFinal);
377}
378
379TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) {
380 LLVMContext C;
381 std::unique_ptr<Module> M = makeLLVMModule(C,
382 IR: R"IR(
383target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
384target triple = "x86_64-pc-linux-gnu"
385declare void @might_throw()
386
387define internal void @callee() {
388entry:
389 call void @might_throw()
390 ret void
391}
392
393define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
394entry:
395 invoke void @callee()
396 to label %cont unwind label %exc
397
398cont:
399 ret i32 0
400
401exc:
402 %exn = landingpad {i8*, i32}
403 cleanup
404 ret i32 1
405}
406
407declare i32 @__gxx_personality_v0(...)
408)IR");
409
410 Function *F1 = M->getFunction(Name: "caller");
411 CallBase* CB = findCall(F&: *F1);
412 EXPECT_NE(CB, nullptr);
413
414 auto FPI = buildFPI(F&: *F1);
415 FunctionPropertiesUpdater FPU(FPI, *CB);
416 InlineFunctionInfo IFI;
417 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
418 EXPECT_TRUE(IR.isSuccess());
419 invalidate(F&: *F1);
420 EXPECT_TRUE(FPU.finishAndTest(FAM));
421 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size());
422 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
423 F1->getInstructionCount());
424}
425
426TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) {
427 LLVMContext C;
428 std::unique_ptr<Module> M = makeLLVMModule(C,
429 IR: R"IR(
430declare void @might_throw()
431
432define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
433entry:
434 invoke void @might_throw()
435 to label %cont unwind label %exc
436
437cont:
438 ret i32 0
439
440exc:
441 %exn = landingpad {i8*, i32}
442 cleanup
443 resume { i8*, i32 } %exn
444}
445
446define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
447entry:
448 %X = invoke i32 @callee()
449 to label %cont unwind label %Handler
450
451cont:
452 ret i32 %X
453
454Handler:
455 %exn = landingpad {i8*, i32}
456 cleanup
457 ret i32 1
458}
459
460declare i32 @__gxx_personality_v0(...)
461)IR");
462
463 Function *F1 = M->getFunction(Name: "caller");
464 CallBase* CB = findCall(F&: *F1);
465 EXPECT_NE(CB, nullptr);
466
467 auto FPI = buildFPI(F&: *F1);
468 FunctionPropertiesUpdater FPU(FPI, *CB);
469 InlineFunctionInfo IFI;
470 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
471 EXPECT_TRUE(IR.isSuccess());
472 invalidate(F&: *F1);
473 EXPECT_TRUE(FPU.finishAndTest(FAM));
474 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1);
475 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
476 F1->getInstructionCount() - 2);
477 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));
478}
479
480TEST_F(FunctionPropertiesAnalysisTest, Rethrow) {
481 LLVMContext C;
482 std::unique_ptr<Module> M = makeLLVMModule(C,
483 IR: R"IR(
484declare void @might_throw()
485
486define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
487entry:
488 invoke void @might_throw()
489 to label %cont unwind label %exc
490
491cont:
492 ret i32 0
493
494exc:
495 %exn = landingpad {i8*, i32}
496 cleanup
497 resume { i8*, i32 } %exn
498}
499
500define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
501entry:
502 %X = invoke i32 @callee()
503 to label %cont unwind label %Handler
504
505cont:
506 ret i32 %X
507
508Handler:
509 %exn = landingpad {i8*, i32}
510 cleanup
511 ret i32 1
512}
513
514declare i32 @__gxx_personality_v0(...)
515)IR");
516
517 Function *F1 = M->getFunction(Name: "caller");
518 CallBase* CB = findCall(F&: *F1);
519 EXPECT_NE(CB, nullptr);
520
521 auto FPI = buildFPI(F&: *F1);
522 FunctionPropertiesUpdater FPU(FPI, *CB);
523 InlineFunctionInfo IFI;
524 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
525 EXPECT_TRUE(IR.isSuccess());
526 invalidate(F&: *F1);
527 EXPECT_TRUE(FPU.finishAndTest(FAM));
528 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1);
529 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
530 F1->getInstructionCount() - 2);
531 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));
532}
533
534TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) {
535 LLVMContext C;
536 std::unique_ptr<Module> M = makeLLVMModule(C,
537 IR: R"IR(
538declare void @external_func()
539
540@exception_type1 = external global i8
541@exception_type2 = external global i8
542
543
544define internal void @inner() personality i8* null {
545 invoke void @external_func()
546 to label %cont unwind label %lpad
547cont:
548 ret void
549lpad:
550 %lp = landingpad i32
551 catch i8* @exception_type1
552 resume i32 %lp
553}
554
555define void @outer() personality i8* null {
556 invoke void @inner()
557 to label %cont unwind label %lpad
558cont:
559 ret void
560lpad:
561 %lp = landingpad i32
562 cleanup
563 catch i8* @exception_type2
564 resume i32 %lp
565}
566
567)IR");
568
569 Function *F1 = M->getFunction(Name: "outer");
570 CallBase* CB = findCall(F&: *F1);
571 EXPECT_NE(CB, nullptr);
572
573 auto FPI = buildFPI(F&: *F1);
574 FunctionPropertiesUpdater FPU(FPI, *CB);
575 InlineFunctionInfo IFI;
576 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
577 EXPECT_TRUE(IR.isSuccess());
578 invalidate(F&: *F1);
579 EXPECT_TRUE(FPU.finishAndTest(FAM));
580 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1);
581 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
582 F1->getInstructionCount() - 2);
583 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));
584}
585
586TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) {
587 LLVMContext C;
588 std::unique_ptr<Module> M = makeLLVMModule(C,
589 IR: R"IR(
590declare void @external_func()
591
592@exception_type1 = external global i8
593@exception_type2 = external global i8
594
595
596define internal void @inner() personality i8* null {
597 invoke void @external_func()
598 to label %cont unwind label %lpad
599cont:
600 ret void
601lpad:
602 %lp = landingpad i32
603 catch i8* @exception_type1
604 resume i32 %lp
605}
606
607define void @outer(i32 %a) personality i8* null {
608entry:
609 %i = icmp slt i32 %a, 0
610 br i1 %i, label %if.then, label %cont
611if.then:
612 invoke void @inner()
613 to label %cont unwind label %lpad
614cont:
615 ret void
616lpad:
617 %lp = landingpad i32
618 cleanup
619 catch i8* @exception_type2
620 resume i32 %lp
621}
622
623)IR");
624
625 Function *F1 = M->getFunction(Name: "outer");
626 CallBase* CB = findCall(F&: *F1);
627 EXPECT_NE(CB, nullptr);
628
629 auto FPI = buildFPI(F&: *F1);
630 FunctionPropertiesUpdater FPU(FPI, *CB);
631 InlineFunctionInfo IFI;
632 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
633 EXPECT_TRUE(IR.isSuccess());
634 invalidate(F&: *F1);
635 EXPECT_TRUE(FPU.finishAndTest(FAM));
636 EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1);
637 EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
638 F1->getInstructionCount() - 2);
639 EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM));
640}
641
642TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) {
643 LLVMContext C;
644 std::unique_ptr<Module> M = makeLLVMModule(C,
645 IR: R"IR(
646target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
647target triple = "x86_64-pc-linux-gnu"
648
649declare i32 @a()
650declare i32 @b()
651
652define i32 @f1(i32 %a) {
653entry:
654 br label %loop
655loop:
656 %i = call i32 @f2(i32 %a)
657 %c = icmp slt i32 %i, %a
658 br i1 %c, label %loop, label %end
659end:
660 %r = phi i32 [%i, %loop], [%a, %entry]
661 ret i32 %r
662}
663
664define i32 @f2(i32 %a) {
665 %cnd = icmp slt i32 %a, 0
666 br i1 %cnd, label %then, label %else
667then:
668 %r1 = call i32 @a()
669 br label %end
670else:
671 %r2 = call i32 @b()
672 br label %end
673end:
674 %r = phi i32 [%r1, %then], [%r2, %else]
675 ret i32 %r
676}
677)IR");
678
679 Function *F1 = M->getFunction(Name: "f1");
680 CallBase *CB = findCall(F&: *F1);
681 EXPECT_NE(CB, nullptr);
682
683 FunctionPropertiesInfo ExpectedInitial;
684 ExpectedInitial.BasicBlockCount = 3;
685 ExpectedInitial.TotalInstructionCount = 6;
686 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
687 ExpectedInitial.Uses = 1;
688 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
689 ExpectedInitial.MaxLoopDepth = 1;
690 ExpectedInitial.TopLevelLoopCount = 1;
691
692 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
693 ExpectedFinal.BasicBlockCount = 6;
694 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
695 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;
696 ExpectedFinal.TotalInstructionCount = 12;
697
698 auto FPI = buildFPI(F&: *F1);
699 EXPECT_EQ(FPI, ExpectedInitial);
700
701 FunctionPropertiesUpdater FPU(FPI, *CB);
702 InlineFunctionInfo IFI;
703 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
704 EXPECT_TRUE(IR.isSuccess());
705 invalidate(F&: *F1);
706 EXPECT_TRUE(FPU.finishAndTest(FAM));
707 EXPECT_EQ(FPI, ExpectedFinal);
708}
709
710TEST_F(FunctionPropertiesAnalysisTest, Unreachable) {
711 LLVMContext C;
712 std::unique_ptr<Module> M = makeLLVMModule(C,
713 IR: R"IR(
714target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
715target triple = "x86_64-pc-linux-gnu"
716
717define i64 @f1(i32 noundef %value) {
718entry:
719 br i1 true, label %cond.true, label %cond.false
720
721cond.true: ; preds = %entry
722 %conv2 = sext i32 %value to i64
723 br label %cond.end
724
725cond.false: ; preds = %entry
726 %call3 = call noundef i64 @f2()
727 br label %extra
728
729extra:
730 br label %extra2
731
732extra2:
733 br label %cond.end
734
735cond.end: ; preds = %cond.false, %cond.true
736 %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ]
737 ret i64 %cond
738}
739
740define i64 @f2() {
741entry:
742 tail call void @llvm.trap()
743 unreachable
744}
745
746declare void @llvm.trap()
747)IR");
748
749 Function *F1 = M->getFunction(Name: "f1");
750 CallBase *CB = findCall(F&: *F1);
751 EXPECT_NE(CB, nullptr);
752
753 FunctionPropertiesInfo ExpectedInitial;
754 ExpectedInitial.BasicBlockCount = 6;
755 ExpectedInitial.TotalInstructionCount = 9;
756 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
757 ExpectedInitial.Uses = 1;
758 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
759
760 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
761 ExpectedFinal.BasicBlockCount = 4;
762 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
763 ExpectedFinal.TotalInstructionCount = 7;
764
765 auto FPI = buildFPI(F&: *F1);
766 EXPECT_EQ(FPI, ExpectedInitial);
767
768 FunctionPropertiesUpdater FPU(FPI, *CB);
769 InlineFunctionInfo IFI;
770 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
771 EXPECT_TRUE(IR.isSuccess());
772 invalidate(F&: *F1);
773 EXPECT_TRUE(FPU.finishAndTest(FAM));
774 EXPECT_EQ(FPI, ExpectedFinal);
775}
776
777TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) {
778 LLVMContext C;
779 std::unique_ptr<Module> M = makeLLVMModule(C,
780 IR: R"IR(
781target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
782target triple = "x86_64-pc-linux-gnu"
783
784define i64 @f1(i32 noundef %value) {
785entry:
786 invoke fastcc void @f2() to label %cont unwind label %lpad
787cont:
788 ret i64 1
789lpad:
790 %lp = landingpad i32 cleanup
791 br label %ehcleanup
792ehcleanup:
793 resume i32 0
794}
795define void @f2() {
796 invoke noundef void @f3() to label %exit unwind label %lpad
797exit:
798 ret void
799lpad:
800 %lp = landingpad i32 cleanup
801 resume i32 %lp
802}
803declare void @f3()
804)IR");
805
806 // The outcome of inlining will be that lpad becomes unreachable. The landing
807 // pad of the invoke inherited from f2 will land on a new bb which will branch
808 // to a bb containing the body of lpad.
809 Function *F1 = M->getFunction(Name: "f1");
810 CallBase *CB = findCall(F&: *F1);
811 EXPECT_NE(CB, nullptr);
812
813 FunctionPropertiesInfo ExpectedInitial;
814 ExpectedInitial.BasicBlockCount = 4;
815 ExpectedInitial.TotalInstructionCount = 5;
816 ExpectedInitial.BlocksReachedFromConditionalInstruction = 0;
817 ExpectedInitial.Uses = 1;
818 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
819
820 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
821 ExpectedFinal.BasicBlockCount = 6;
822 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
823 ExpectedFinal.TotalInstructionCount = 8;
824
825 auto FPI = buildFPI(F&: *F1);
826 EXPECT_EQ(FPI, ExpectedInitial);
827
828 FunctionPropertiesUpdater FPU(FPI, *CB);
829 InlineFunctionInfo IFI;
830 auto IR = llvm::InlineFunction(CB&: *CB, IFI);
831 EXPECT_TRUE(IR.isSuccess());
832 invalidate(F&: *F1);
833 EXPECT_TRUE(FPU.finishAndTest(FAM));
834 EXPECT_EQ(FPI, ExpectedFinal);
835}
836
837TEST_F(FunctionPropertiesAnalysisTest, DetailedOperandCount) {
838 LLVMContext C;
839 std::unique_ptr<Module> M = makeLLVMModule(C,
840 IR: R"IR(
841@a = global i64 1
842
843define i64 @f1(i64 %e) {
844 %b = load i64, i64* @a
845 %c = add i64 %b, 2
846 %d = call i64 asm "mov $1,$0", "=r,r" (i64 %c)
847 %f = add i64 %d, %e
848 ret i64 %f
849}
850)IR");
851
852 Function *F1 = M->getFunction(Name: "f1");
853 EnableDetailedFunctionProperties.setValue(V: true);
854 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
855 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0);
856 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 0);
857 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0);
858 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 0);
859 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0);
860 EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0);
861 EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0);
862 EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0);
863 EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 1);
864 EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0);
865 EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0);
866 EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 4);
867 EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 1);
868 EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0);
869 EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0);
870 EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 4);
871 EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 0);
872 EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 1);
873 EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 1);
874 EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 1);
875 EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0);
876 EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 0);
877 EXPECT_EQ(DetailedF1Properties.ControlFlowEdgeCount, 0);
878 EXPECT_EQ(DetailedF1Properties.UnconditionalBranchCount, 0);
879 EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0);
880 EXPECT_EQ(DetailedF1Properties.DirectCallCount, 1);
881 EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0);
882 EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 1);
883 EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 0);
884 EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0);
885 EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0);
886 EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0);
887 EnableDetailedFunctionProperties.setValue(V: false);
888}
889
890TEST_F(FunctionPropertiesAnalysisTest, IntrinsicCount) {
891 LLVMContext C;
892 std::unique_ptr<Module> M = makeLLVMModule(C,
893 IR: R"IR(
894define float @f1(float %a) {
895 %b = call float @llvm.cos.f32(float %a)
896 ret float %b
897}
898declare float @llvm.cos.f32(float)
899)IR");
900
901 Function *F1 = M->getFunction(Name: "f1");
902 EnableDetailedFunctionProperties.setValue(V: true);
903 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
904 EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 1);
905 EXPECT_EQ(DetailedF1Properties.DirectCallCount, 1);
906 EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0);
907 EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 0);
908 EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 1);
909 EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0);
910 EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0);
911 EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0);
912 EnableDetailedFunctionProperties.setValue(V: false);
913}
914
915TEST_F(FunctionPropertiesAnalysisTest, FunctionCallMetrics) {
916 LLVMContext C;
917 std::unique_ptr<Module> M = makeLLVMModule(C,
918 IR: R"IR(
919define i64 @f1(i64 %a) {
920 %b = call i64 @f2(i64 %a, i64 %a, i64 %a, i64 %a, i64 %a)
921 %c = call ptr @f3()
922 call void @f4(ptr %c)
923 %d = call float @f5()
924 %e = call i64 %c(i64 %b)
925 ret i64 %b
926}
927
928declare i64 @f2(i64,i64,i64,i64,i64)
929declare ptr @f3()
930declare void @f4(ptr)
931declare float @f5()
932)IR");
933
934 Function *F1 = M->getFunction(Name: "f1");
935 EnableDetailedFunctionProperties.setValue(V: true);
936 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
937 EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0);
938 EXPECT_EQ(DetailedF1Properties.DirectCallCount, 4);
939 EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 1);
940 EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 2);
941 EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 1);
942 EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 1);
943 EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 1);
944 EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 1);
945 EnableDetailedFunctionProperties.setValue(V: false);
946}
947
948TEST_F(FunctionPropertiesAnalysisTest, CriticalEdge) {
949 LLVMContext C;
950 std::unique_ptr<Module> M = makeLLVMModule(C,
951 IR: R"IR(
952define i64 @f1(i64 %a) {
953 %b = icmp eq i64 %a, 1
954 br i1 %b, label %TopBlock1, label %TopBlock2
955TopBlock1:
956 %c = add i64 %a, 1
957 %e = icmp eq i64 %c, 2
958 br i1 %e, label %BottomBlock1, label %BottomBlock2
959TopBlock2:
960 %d = add i64 %a, 2
961 br label %BottomBlock2
962BottomBlock1:
963 ret i64 0
964BottomBlock2:
965 %f = phi i64 [ %c, %TopBlock1 ], [ %d, %TopBlock2 ]
966 ret i64 %f
967}
968)IR");
969
970 Function *F1 = M->getFunction(Name: "f1");
971 EnableDetailedFunctionProperties.setValue(V: true);
972 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
973 EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 1);
974 EnableDetailedFunctionProperties.setValue(V: false);
975}
976
977
978TEST_F(FunctionPropertiesAnalysisTest, FunctionReturnVectors) {
979 LLVMContext C;
980 std::unique_ptr<Module> M = makeLLVMModule(C,
981 IR: R"IR(
982define <4 x i64> @f1(<4 x i64> %a) {
983 %b = call <4 x i64> @f2()
984 %c = call <4 x float> @f3()
985 %d = call <4 x ptr> @f4()
986 ret <4 x i64> %b
987}
988
989declare <4 x i64> @f2()
990declare <4 x float> @f3()
991declare <4 x ptr> @f4()
992)IR");
993
994 Function *F1 = M->getFunction(Name: "f1");
995 EnableDetailedFunctionProperties.setValue(V: true);
996 FunctionPropertiesInfo DetailedF1Properties = buildFPI(F&: *F1);
997 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorIntCount, 1);
998 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorFloatCount, 1);
999 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorPointerCount, 1);
1000 EnableDetailedFunctionProperties.setValue(V: false);
1001}
1002} // end anonymous namespace
1003

source code of llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp