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 | |
25 | using namespace llvm; |
26 | |
27 | namespace llvm { |
28 | extern cl::opt<bool> EnableDetailedFunctionProperties; |
29 | extern cl::opt<bool> BigBasicBlockInstructionThreshold; |
30 | extern cl::opt<bool> MediumBasicBlockInstrutionThreshold; |
31 | } // namespace llvm |
32 | |
33 | namespace { |
34 | |
35 | class FunctionPropertiesAnalysisTest : public testing::Test { |
36 | public: |
37 | FunctionPropertiesAnalysisTest() { |
38 | FAM.registerPass(PassBuilder: [&] { return DominatorTreeAnalysis(); }); |
39 | FAM.registerPass(PassBuilder: [&] { return LoopAnalysis(); }); |
40 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(); }); |
41 | } |
42 | |
43 | protected: |
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 | |
75 | TEST_F(FunctionPropertiesAnalysisTest, BasicTest) { |
76 | LLVMContext C; |
77 | std::unique_ptr<Module> M = makeLLVMModule(C, |
78 | IR: R"IR( |
79 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
80 | target triple = "x86_64-pc-linux-gnu" |
81 | declare i32 @f1(i32) |
82 | declare i32 @f2(i32) |
83 | define i32 @branches(i32) { |
84 | %cond = icmp slt i32 %0, 3 |
85 | br i1 %cond, label %then, label %else |
86 | then: |
87 | %ret.1 = call i32 @f1(i32 %0) |
88 | br label %last.block |
89 | else: |
90 | %ret.2 = call i32 @f2(i32 %0) |
91 | br label %last.block |
92 | last.block: |
93 | %ret = phi i32 [%ret.1, %then], [%ret.2, %else] |
94 | ret i32 %ret |
95 | } |
96 | define 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 | |
164 | TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) { |
165 | LLVMContext C; |
166 | std::unique_ptr<Module> M = makeLLVMModule(C, |
167 | IR: R"IR( |
168 | define i64 @f1() { |
169 | br i1 0, label %br1, label %finally |
170 | br1: |
171 | ret i64 0 |
172 | finally: |
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 | |
215 | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) { |
216 | LLVMContext C; |
217 | std::unique_ptr<Module> M = makeLLVMModule(C, |
218 | IR: R"IR( |
219 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
220 | target triple = "x86_64-pc-linux-gnu" |
221 | define i32 @f1(i32 %a) { |
222 | %b = call i32 @f2(i32 %a) |
223 | %c = add i32 %b, 2 |
224 | ret i32 %c |
225 | } |
226 | |
227 | define 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 | |
258 | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) { |
259 | LLVMContext C; |
260 | std::unique_ptr<Module> M = makeLLVMModule(C, |
261 | IR: R"IR( |
262 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
263 | target triple = "x86_64-pc-linux-gnu" |
264 | define i32 @f1(i32 %a) { |
265 | entry: |
266 | %i = icmp slt i32 %a, 0 |
267 | br i1 %i, label %if.then, label %if.else |
268 | if.then: |
269 | %b = call i32 @f2(i32 %a) |
270 | %c1 = add i32 %b, 2 |
271 | br label %end |
272 | if.else: |
273 | %c2 = add i32 %a, 1 |
274 | br label %end |
275 | end: |
276 | %ret = phi i32 [%c1, %if.then],[%c2, %if.else] |
277 | ret i32 %ret |
278 | } |
279 | |
280 | define 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 | |
312 | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) { |
313 | LLVMContext C; |
314 | std::unique_ptr<Module> M = makeLLVMModule(C, |
315 | IR: R"IR( |
316 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
317 | target triple = "x86_64-pc-linux-gnu" |
318 | define i32 @f1(i32 %a) { |
319 | entry: |
320 | %i = icmp slt i32 %a, 0 |
321 | br i1 %i, label %if.then, label %if.else |
322 | if.then: |
323 | %b = call i32 @f2(i32 %a) |
324 | %c1 = add i32 %b, 2 |
325 | br label %end |
326 | if.else: |
327 | %c2 = add i32 %a, 1 |
328 | br label %end |
329 | end: |
330 | %ret = phi i32 [%c1, %if.then],[%c2, %if.else] |
331 | ret i32 %ret |
332 | } |
333 | |
334 | define i32 @f2(i32 %a) { |
335 | entry: |
336 | br label %loop |
337 | loop: |
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 |
343 | exit: |
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 | |
379 | TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) { |
380 | LLVMContext C; |
381 | std::unique_ptr<Module> M = makeLLVMModule(C, |
382 | IR: R"IR( |
383 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
384 | target triple = "x86_64-pc-linux-gnu" |
385 | declare void @might_throw() |
386 | |
387 | define internal void @callee() { |
388 | entry: |
389 | call void @might_throw() |
390 | ret void |
391 | } |
392 | |
393 | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { |
394 | entry: |
395 | invoke void @callee() |
396 | to label %cont unwind label %exc |
397 | |
398 | cont: |
399 | ret i32 0 |
400 | |
401 | exc: |
402 | %exn = landingpad {i8*, i32} |
403 | cleanup |
404 | ret i32 1 |
405 | } |
406 | |
407 | declare 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 | |
426 | TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) { |
427 | LLVMContext C; |
428 | std::unique_ptr<Module> M = makeLLVMModule(C, |
429 | IR: R"IR( |
430 | declare void @might_throw() |
431 | |
432 | define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { |
433 | entry: |
434 | invoke void @might_throw() |
435 | to label %cont unwind label %exc |
436 | |
437 | cont: |
438 | ret i32 0 |
439 | |
440 | exc: |
441 | %exn = landingpad {i8*, i32} |
442 | cleanup |
443 | resume { i8*, i32 } %exn |
444 | } |
445 | |
446 | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { |
447 | entry: |
448 | %X = invoke i32 @callee() |
449 | to label %cont unwind label %Handler |
450 | |
451 | cont: |
452 | ret i32 %X |
453 | |
454 | Handler: |
455 | %exn = landingpad {i8*, i32} |
456 | cleanup |
457 | ret i32 1 |
458 | } |
459 | |
460 | declare 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 | |
480 | TEST_F(FunctionPropertiesAnalysisTest, Rethrow) { |
481 | LLVMContext C; |
482 | std::unique_ptr<Module> M = makeLLVMModule(C, |
483 | IR: R"IR( |
484 | declare void @might_throw() |
485 | |
486 | define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { |
487 | entry: |
488 | invoke void @might_throw() |
489 | to label %cont unwind label %exc |
490 | |
491 | cont: |
492 | ret i32 0 |
493 | |
494 | exc: |
495 | %exn = landingpad {i8*, i32} |
496 | cleanup |
497 | resume { i8*, i32 } %exn |
498 | } |
499 | |
500 | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { |
501 | entry: |
502 | %X = invoke i32 @callee() |
503 | to label %cont unwind label %Handler |
504 | |
505 | cont: |
506 | ret i32 %X |
507 | |
508 | Handler: |
509 | %exn = landingpad {i8*, i32} |
510 | cleanup |
511 | ret i32 1 |
512 | } |
513 | |
514 | declare 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 | |
534 | TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) { |
535 | LLVMContext C; |
536 | std::unique_ptr<Module> M = makeLLVMModule(C, |
537 | IR: R"IR( |
538 | declare void @external_func() |
539 | |
540 | @exception_type1 = external global i8 |
541 | @exception_type2 = external global i8 |
542 | |
543 | |
544 | define internal void @inner() personality i8* null { |
545 | invoke void @external_func() |
546 | to label %cont unwind label %lpad |
547 | cont: |
548 | ret void |
549 | lpad: |
550 | %lp = landingpad i32 |
551 | catch i8* @exception_type1 |
552 | resume i32 %lp |
553 | } |
554 | |
555 | define void @outer() personality i8* null { |
556 | invoke void @inner() |
557 | to label %cont unwind label %lpad |
558 | cont: |
559 | ret void |
560 | lpad: |
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 | |
586 | TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) { |
587 | LLVMContext C; |
588 | std::unique_ptr<Module> M = makeLLVMModule(C, |
589 | IR: R"IR( |
590 | declare void @external_func() |
591 | |
592 | @exception_type1 = external global i8 |
593 | @exception_type2 = external global i8 |
594 | |
595 | |
596 | define internal void @inner() personality i8* null { |
597 | invoke void @external_func() |
598 | to label %cont unwind label %lpad |
599 | cont: |
600 | ret void |
601 | lpad: |
602 | %lp = landingpad i32 |
603 | catch i8* @exception_type1 |
604 | resume i32 %lp |
605 | } |
606 | |
607 | define void @outer(i32 %a) personality i8* null { |
608 | entry: |
609 | %i = icmp slt i32 %a, 0 |
610 | br i1 %i, label %if.then, label %cont |
611 | if.then: |
612 | invoke void @inner() |
613 | to label %cont unwind label %lpad |
614 | cont: |
615 | ret void |
616 | lpad: |
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 | |
642 | TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) { |
643 | LLVMContext C; |
644 | std::unique_ptr<Module> M = makeLLVMModule(C, |
645 | IR: R"IR( |
646 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
647 | target triple = "x86_64-pc-linux-gnu" |
648 | |
649 | declare i32 @a() |
650 | declare i32 @b() |
651 | |
652 | define i32 @f1(i32 %a) { |
653 | entry: |
654 | br label %loop |
655 | loop: |
656 | %i = call i32 @f2(i32 %a) |
657 | %c = icmp slt i32 %i, %a |
658 | br i1 %c, label %loop, label %end |
659 | end: |
660 | %r = phi i32 [%i, %loop], [%a, %entry] |
661 | ret i32 %r |
662 | } |
663 | |
664 | define i32 @f2(i32 %a) { |
665 | %cnd = icmp slt i32 %a, 0 |
666 | br i1 %cnd, label %then, label %else |
667 | then: |
668 | %r1 = call i32 @a() |
669 | br label %end |
670 | else: |
671 | %r2 = call i32 @b() |
672 | br label %end |
673 | end: |
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 | |
710 | TEST_F(FunctionPropertiesAnalysisTest, Unreachable) { |
711 | LLVMContext C; |
712 | std::unique_ptr<Module> M = makeLLVMModule(C, |
713 | IR: R"IR( |
714 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
715 | target triple = "x86_64-pc-linux-gnu" |
716 | |
717 | define i64 @f1(i32 noundef %value) { |
718 | entry: |
719 | br i1 true, label %cond.true, label %cond.false |
720 | |
721 | cond.true: ; preds = %entry |
722 | %conv2 = sext i32 %value to i64 |
723 | br label %cond.end |
724 | |
725 | cond.false: ; preds = %entry |
726 | %call3 = call noundef i64 @f2() |
727 | br label %extra |
728 | |
729 | extra: |
730 | br label %extra2 |
731 | |
732 | extra2: |
733 | br label %cond.end |
734 | |
735 | cond.end: ; preds = %cond.false, %cond.true |
736 | %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ] |
737 | ret i64 %cond |
738 | } |
739 | |
740 | define i64 @f2() { |
741 | entry: |
742 | tail call void @llvm.trap() |
743 | unreachable |
744 | } |
745 | |
746 | declare 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 | |
777 | TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) { |
778 | LLVMContext C; |
779 | std::unique_ptr<Module> M = makeLLVMModule(C, |
780 | IR: R"IR( |
781 | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
782 | target triple = "x86_64-pc-linux-gnu" |
783 | |
784 | define i64 @f1(i32 noundef %value) { |
785 | entry: |
786 | invoke fastcc void @f2() to label %cont unwind label %lpad |
787 | cont: |
788 | ret i64 1 |
789 | lpad: |
790 | %lp = landingpad i32 cleanup |
791 | br label %ehcleanup |
792 | ehcleanup: |
793 | resume i32 0 |
794 | } |
795 | define void @f2() { |
796 | invoke noundef void @f3() to label %exit unwind label %lpad |
797 | exit: |
798 | ret void |
799 | lpad: |
800 | %lp = landingpad i32 cleanup |
801 | resume i32 %lp |
802 | } |
803 | declare 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 | |
837 | TEST_F(FunctionPropertiesAnalysisTest, DetailedOperandCount) { |
838 | LLVMContext C; |
839 | std::unique_ptr<Module> M = makeLLVMModule(C, |
840 | IR: R"IR( |
841 | @a = global i64 1 |
842 | |
843 | define 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 | |
890 | TEST_F(FunctionPropertiesAnalysisTest, IntrinsicCount) { |
891 | LLVMContext C; |
892 | std::unique_ptr<Module> M = makeLLVMModule(C, |
893 | IR: R"IR( |
894 | define float @f1(float %a) { |
895 | %b = call float @llvm.cos.f32(float %a) |
896 | ret float %b |
897 | } |
898 | declare 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 | |
915 | TEST_F(FunctionPropertiesAnalysisTest, FunctionCallMetrics) { |
916 | LLVMContext C; |
917 | std::unique_ptr<Module> M = makeLLVMModule(C, |
918 | IR: R"IR( |
919 | define 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 | |
928 | declare i64 @f2(i64,i64,i64,i64,i64) |
929 | declare ptr @f3() |
930 | declare void @f4(ptr) |
931 | declare 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 | |
948 | TEST_F(FunctionPropertiesAnalysisTest, CriticalEdge) { |
949 | LLVMContext C; |
950 | std::unique_ptr<Module> M = makeLLVMModule(C, |
951 | IR: R"IR( |
952 | define i64 @f1(i64 %a) { |
953 | %b = icmp eq i64 %a, 1 |
954 | br i1 %b, label %TopBlock1, label %TopBlock2 |
955 | TopBlock1: |
956 | %c = add i64 %a, 1 |
957 | %e = icmp eq i64 %c, 2 |
958 | br i1 %e, label %BottomBlock1, label %BottomBlock2 |
959 | TopBlock2: |
960 | %d = add i64 %a, 2 |
961 | br label %BottomBlock2 |
962 | BottomBlock1: |
963 | ret i64 0 |
964 | BottomBlock2: |
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 | |
978 | TEST_F(FunctionPropertiesAnalysisTest, FunctionReturnVectors) { |
979 | LLVMContext C; |
980 | std::unique_ptr<Module> M = makeLLVMModule(C, |
981 | IR: R"IR( |
982 | define <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 | |
989 | declare <4 x i64> @f2() |
990 | declare <4 x float> @f3() |
991 | declare <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 | |