Fix test failures introduced by PR #113697 (#116941)
[llvm-project.git] / llvm / unittests / Analysis / FunctionPropertiesAnalysisTest.cpp
blob574ad7c8430e3e9d56dff16edae2469c1475b510
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 //===----------------------------------------------------------------------===//
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>
25 using namespace llvm;
27 namespace llvm {
28 extern cl::opt<bool> EnableDetailedFunctionProperties;
29 extern cl::opt<bool> BigBasicBlockInstructionThreshold;
30 extern cl::opt<bool> MediumBasicBlockInstrutionThreshold;
31 } // namespace llvm
33 namespace {
35 class FunctionPropertiesAnalysisTest : public testing::Test {
36 public:
37 FunctionPropertiesAnalysisTest() {
38 FAM.registerPass([&] { return DominatorTreeAnalysis(); });
39 FAM.registerPass([&] { return LoopAnalysis(); });
40 FAM.registerPass([&] { return PassInstrumentationAnalysis(); });
43 protected:
44 std::unique_ptr<DominatorTree> DT;
45 std::unique_ptr<LoopInfo> LI;
46 FunctionAnalysisManager FAM;
48 FunctionPropertiesInfo buildFPI(Function &F) {
49 return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM);
52 void invalidate(Function &F) {
53 PreservedAnalyses PA = PreservedAnalyses::none();
54 FAM.invalidate(F, PA);
57 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
58 SMDiagnostic Err;
59 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
60 if (!Mod)
61 Err.print("MLAnalysisTests", errs());
62 return Mod;
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>(&I))
69 if (!Name || CB->getName() == Name)
70 return CB;
71 return nullptr;
75 TEST_F(FunctionPropertiesAnalysisTest, BasicTest) {
76 LLVMContext C;
77 std::unique_ptr<Module> M = makeLLVMModule(C,
78 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
96 define internal i32 @top() {
97 %1 = call i32 @branches(i32 2)
98 %2 = call i32 @f1(i32 %1)
99 ret i32 %2
101 )IR");
103 Function *BranchesFunction = M->getFunction("branches");
104 FunctionPropertiesInfo BranchesFeatures = buildFPI(*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);
116 Function *TopFunction = M->getFunction("top");
117 FunctionPropertiesInfo TopFeatures = buildFPI(*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);
127 EnableDetailedFunctionProperties.setValue(true);
128 FunctionPropertiesInfo DetailedBranchesFeatures = buildFPI(*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(false);
164 TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) {
165 LLVMContext C;
166 std::unique_ptr<Module> M = makeLLVMModule(C,
167 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
175 )IR");
177 Function *F1 = M->getFunction("f1");
178 EnableDetailedFunctionProperties.setValue(true);
179 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*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(false);
215 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) {
216 LLVMContext C;
217 std::unique_ptr<Module> M = makeLLVMModule(C,
218 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
227 define i32 @f2(i32 %a) {
228 %b = add i32 %a, 1
229 ret i32 %b
231 )IR");
233 Function *F1 = M->getFunction("f1");
234 CallBase* CB = findCall(*F1, "b");
235 EXPECT_NE(CB, nullptr);
237 FunctionPropertiesInfo ExpectedInitial;
238 ExpectedInitial.BasicBlockCount = 1;
239 ExpectedInitial.TotalInstructionCount = 3;
240 ExpectedInitial.Uses = 1;
241 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
243 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
244 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
246 auto FPI = buildFPI(*F1);
247 EXPECT_EQ(FPI, ExpectedInitial);
249 FunctionPropertiesUpdater FPU(FPI, *CB);
250 InlineFunctionInfo IFI;
251 auto IR = llvm::InlineFunction(*CB, IFI);
252 EXPECT_TRUE(IR.isSuccess());
253 invalidate(*F1);
254 EXPECT_TRUE(FPU.finishAndTest(FAM));
255 EXPECT_EQ(FPI, ExpectedFinal);
258 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) {
259 LLVMContext C;
260 std::unique_ptr<Module> M = makeLLVMModule(C,
261 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
280 define i32 @f2(i32 %a) {
281 %b = add i32 %a, 1
282 ret i32 %b
284 )IR");
286 Function *F1 = M->getFunction("f1");
287 CallBase* CB = findCall(*F1, "b");
288 EXPECT_NE(CB, nullptr);
290 FunctionPropertiesInfo ExpectedInitial;
291 ExpectedInitial.BasicBlockCount = 4;
292 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
293 ExpectedInitial.TotalInstructionCount = 9;
294 ExpectedInitial.Uses = 1;
295 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
297 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
298 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
300 auto FPI = buildFPI(*F1);
301 EXPECT_EQ(FPI, ExpectedInitial);
303 FunctionPropertiesUpdater FPU(FPI, *CB);
304 InlineFunctionInfo IFI;
305 auto IR = llvm::InlineFunction(*CB, IFI);
306 EXPECT_TRUE(IR.isSuccess());
307 invalidate(*F1);
308 EXPECT_TRUE(FPU.finishAndTest(FAM));
309 EXPECT_EQ(FPI, ExpectedFinal);
312 TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) {
313 LLVMContext C;
314 std::unique_ptr<Module> M = makeLLVMModule(C,
315 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
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
346 )IR");
348 Function *F1 = M->getFunction("f1");
349 CallBase* CB = findCall(*F1, "b");
350 EXPECT_NE(CB, nullptr);
352 FunctionPropertiesInfo ExpectedInitial;
353 ExpectedInitial.BasicBlockCount = 4;
354 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
355 ExpectedInitial.TotalInstructionCount = 9;
356 ExpectedInitial.Uses = 1;
357 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
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;
367 auto FPI = buildFPI(*F1);
368 EXPECT_EQ(FPI, ExpectedInitial);
369 FunctionPropertiesUpdater FPU(FPI, *CB);
370 InlineFunctionInfo IFI;
372 auto IR = llvm::InlineFunction(*CB, IFI);
373 EXPECT_TRUE(IR.isSuccess());
374 invalidate(*F1);
375 EXPECT_TRUE(FPU.finishAndTest(FAM));
376 EXPECT_EQ(FPI, ExpectedFinal);
379 TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) {
380 LLVMContext C;
381 std::unique_ptr<Module> M = makeLLVMModule(C,
382 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()
387 define internal void @callee() {
388 entry:
389 call void @might_throw()
390 ret void
393 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
394 entry:
395 invoke void @callee()
396 to label %cont unwind label %exc
398 cont:
399 ret i32 0
401 exc:
402 %exn = landingpad {i8*, i32}
403 cleanup
404 ret i32 1
407 declare i32 @__gxx_personality_v0(...)
408 )IR");
410 Function *F1 = M->getFunction("caller");
411 CallBase* CB = findCall(*F1);
412 EXPECT_NE(CB, nullptr);
414 auto FPI = buildFPI(*F1);
415 FunctionPropertiesUpdater FPU(FPI, *CB);
416 InlineFunctionInfo IFI;
417 auto IR = llvm::InlineFunction(*CB, IFI);
418 EXPECT_TRUE(IR.isSuccess());
419 invalidate(*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());
426 TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) {
427 LLVMContext C;
428 std::unique_ptr<Module> M = makeLLVMModule(C,
429 R"IR(
430 declare void @might_throw()
432 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
433 entry:
434 invoke void @might_throw()
435 to label %cont unwind label %exc
437 cont:
438 ret i32 0
440 exc:
441 %exn = landingpad {i8*, i32}
442 cleanup
443 resume { i8*, i32 } %exn
446 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
447 entry:
448 %X = invoke i32 @callee()
449 to label %cont unwind label %Handler
451 cont:
452 ret i32 %X
454 Handler:
455 %exn = landingpad {i8*, i32}
456 cleanup
457 ret i32 1
460 declare i32 @__gxx_personality_v0(...)
461 )IR");
463 Function *F1 = M->getFunction("caller");
464 CallBase* CB = findCall(*F1);
465 EXPECT_NE(CB, nullptr);
467 auto FPI = buildFPI(*F1);
468 FunctionPropertiesUpdater FPU(FPI, *CB);
469 InlineFunctionInfo IFI;
470 auto IR = llvm::InlineFunction(*CB, IFI);
471 EXPECT_TRUE(IR.isSuccess());
472 invalidate(*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));
480 TEST_F(FunctionPropertiesAnalysisTest, Rethrow) {
481 LLVMContext C;
482 std::unique_ptr<Module> M = makeLLVMModule(C,
483 R"IR(
484 declare void @might_throw()
486 define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
487 entry:
488 invoke void @might_throw()
489 to label %cont unwind label %exc
491 cont:
492 ret i32 0
494 exc:
495 %exn = landingpad {i8*, i32}
496 cleanup
497 resume { i8*, i32 } %exn
500 define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
501 entry:
502 %X = invoke i32 @callee()
503 to label %cont unwind label %Handler
505 cont:
506 ret i32 %X
508 Handler:
509 %exn = landingpad {i8*, i32}
510 cleanup
511 ret i32 1
514 declare i32 @__gxx_personality_v0(...)
515 )IR");
517 Function *F1 = M->getFunction("caller");
518 CallBase* CB = findCall(*F1);
519 EXPECT_NE(CB, nullptr);
521 auto FPI = buildFPI(*F1);
522 FunctionPropertiesUpdater FPU(FPI, *CB);
523 InlineFunctionInfo IFI;
524 auto IR = llvm::InlineFunction(*CB, IFI);
525 EXPECT_TRUE(IR.isSuccess());
526 invalidate(*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));
534 TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) {
535 LLVMContext C;
536 std::unique_ptr<Module> M = makeLLVMModule(C,
537 R"IR(
538 declare void @external_func()
540 @exception_type1 = external global i8
541 @exception_type2 = external global i8
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
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
567 )IR");
569 Function *F1 = M->getFunction("outer");
570 CallBase* CB = findCall(*F1);
571 EXPECT_NE(CB, nullptr);
573 auto FPI = buildFPI(*F1);
574 FunctionPropertiesUpdater FPU(FPI, *CB);
575 InlineFunctionInfo IFI;
576 auto IR = llvm::InlineFunction(*CB, IFI);
577 EXPECT_TRUE(IR.isSuccess());
578 invalidate(*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));
586 TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) {
587 LLVMContext C;
588 std::unique_ptr<Module> M = makeLLVMModule(C,
589 R"IR(
590 declare void @external_func()
592 @exception_type1 = external global i8
593 @exception_type2 = external global i8
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
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
623 )IR");
625 Function *F1 = M->getFunction("outer");
626 CallBase* CB = findCall(*F1);
627 EXPECT_NE(CB, nullptr);
629 auto FPI = buildFPI(*F1);
630 FunctionPropertiesUpdater FPU(FPI, *CB);
631 InlineFunctionInfo IFI;
632 auto IR = llvm::InlineFunction(*CB, IFI);
633 EXPECT_TRUE(IR.isSuccess());
634 invalidate(*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));
642 TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) {
643 LLVMContext C;
644 std::unique_ptr<Module> M = makeLLVMModule(C,
645 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"
649 declare i32 @a()
650 declare i32 @b()
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
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
677 )IR");
679 Function *F1 = M->getFunction("f1");
680 CallBase *CB = findCall(*F1);
681 EXPECT_NE(CB, nullptr);
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;
692 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
693 ExpectedFinal.BasicBlockCount = 6;
694 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
695 ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;
696 ExpectedFinal.TotalInstructionCount = 12;
698 auto FPI = buildFPI(*F1);
699 EXPECT_EQ(FPI, ExpectedInitial);
701 FunctionPropertiesUpdater FPU(FPI, *CB);
702 InlineFunctionInfo IFI;
703 auto IR = llvm::InlineFunction(*CB, IFI);
704 EXPECT_TRUE(IR.isSuccess());
705 invalidate(*F1);
706 EXPECT_TRUE(FPU.finishAndTest(FAM));
707 EXPECT_EQ(FPI, ExpectedFinal);
710 TEST_F(FunctionPropertiesAnalysisTest, Unreachable) {
711 LLVMContext C;
712 std::unique_ptr<Module> M = makeLLVMModule(C,
713 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"
717 define i64 @f1(i32 noundef %value) {
718 entry:
719 br i1 true, label %cond.true, label %cond.false
721 cond.true: ; preds = %entry
722 %conv2 = sext i32 %value to i64
723 br label %cond.end
725 cond.false: ; preds = %entry
726 %call3 = call noundef i64 @f2()
727 br label %extra
729 extra:
730 br label %extra2
732 extra2:
733 br label %cond.end
735 cond.end: ; preds = %cond.false, %cond.true
736 %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ]
737 ret i64 %cond
740 define i64 @f2() {
741 entry:
742 tail call void @llvm.trap()
743 unreachable
746 declare void @llvm.trap()
747 )IR");
749 Function *F1 = M->getFunction("f1");
750 CallBase *CB = findCall(*F1);
751 EXPECT_NE(CB, nullptr);
753 FunctionPropertiesInfo ExpectedInitial;
754 ExpectedInitial.BasicBlockCount = 6;
755 ExpectedInitial.TotalInstructionCount = 9;
756 ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
757 ExpectedInitial.Uses = 1;
758 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
760 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
761 ExpectedFinal.BasicBlockCount = 4;
762 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
763 ExpectedFinal.TotalInstructionCount = 7;
765 auto FPI = buildFPI(*F1);
766 EXPECT_EQ(FPI, ExpectedInitial);
768 FunctionPropertiesUpdater FPU(FPI, *CB);
769 InlineFunctionInfo IFI;
770 auto IR = llvm::InlineFunction(*CB, IFI);
771 EXPECT_TRUE(IR.isSuccess());
772 invalidate(*F1);
773 EXPECT_TRUE(FPU.finishAndTest(FAM));
774 EXPECT_EQ(FPI, ExpectedFinal);
777 TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) {
778 LLVMContext C;
779 std::unique_ptr<Module> M = makeLLVMModule(C,
780 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"
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
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
803 declare void @f3()
804 )IR");
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("f1");
810 CallBase *CB = findCall(*F1);
811 EXPECT_NE(CB, nullptr);
813 FunctionPropertiesInfo ExpectedInitial;
814 ExpectedInitial.BasicBlockCount = 4;
815 ExpectedInitial.TotalInstructionCount = 5;
816 ExpectedInitial.BlocksReachedFromConditionalInstruction = 0;
817 ExpectedInitial.Uses = 1;
818 ExpectedInitial.DirectCallsToDefinedFunctions = 1;
820 FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
821 ExpectedFinal.BasicBlockCount = 6;
822 ExpectedFinal.DirectCallsToDefinedFunctions = 0;
823 ExpectedFinal.TotalInstructionCount = 8;
825 auto FPI = buildFPI(*F1);
826 EXPECT_EQ(FPI, ExpectedInitial);
828 FunctionPropertiesUpdater FPU(FPI, *CB);
829 InlineFunctionInfo IFI;
830 auto IR = llvm::InlineFunction(*CB, IFI);
831 EXPECT_TRUE(IR.isSuccess());
832 invalidate(*F1);
833 EXPECT_TRUE(FPU.finishAndTest(FAM));
834 EXPECT_EQ(FPI, ExpectedFinal);
837 TEST_F(FunctionPropertiesAnalysisTest, DetailedOperandCount) {
838 LLVMContext C;
839 std::unique_ptr<Module> M = makeLLVMModule(C,
840 R"IR(
841 @a = global i64 1
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
850 )IR");
852 Function *F1 = M->getFunction("f1");
853 EnableDetailedFunctionProperties.setValue(true);
854 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*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(false);
890 TEST_F(FunctionPropertiesAnalysisTest, IntrinsicCount) {
891 LLVMContext C;
892 std::unique_ptr<Module> M = makeLLVMModule(C,
893 R"IR(
894 define float @f1(float %a) {
895 %b = call float @llvm.cos.f32(float %a)
896 ret float %b
898 declare float @llvm.cos.f32(float)
899 )IR");
901 Function *F1 = M->getFunction("f1");
902 EnableDetailedFunctionProperties.setValue(true);
903 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*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(false);
915 TEST_F(FunctionPropertiesAnalysisTest, FunctionCallMetrics) {
916 LLVMContext C;
917 std::unique_ptr<Module> M = makeLLVMModule(C,
918 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
928 declare i64 @f2(i64,i64,i64,i64,i64)
929 declare ptr @f3()
930 declare void @f4(ptr)
931 declare float @f5()
932 )IR");
934 Function *F1 = M->getFunction("f1");
935 EnableDetailedFunctionProperties.setValue(true);
936 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*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(false);
948 TEST_F(FunctionPropertiesAnalysisTest, CriticalEdge) {
949 LLVMContext C;
950 std::unique_ptr<Module> M = makeLLVMModule(C,
951 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
968 )IR");
970 Function *F1 = M->getFunction("f1");
971 EnableDetailedFunctionProperties.setValue(true);
972 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1);
973 EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 1);
974 EnableDetailedFunctionProperties.setValue(false);
978 TEST_F(FunctionPropertiesAnalysisTest, FunctionReturnVectors) {
979 LLVMContext C;
980 std::unique_ptr<Module> M = makeLLVMModule(C,
981 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
989 declare <4 x i64> @f2()
990 declare <4 x float> @f3()
991 declare <4 x ptr> @f4()
992 )IR");
994 Function *F1 = M->getFunction("f1");
995 EnableDetailedFunctionProperties.setValue(true);
996 FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1);
997 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorIntCount, 1);
998 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorFloatCount, 1);
999 EXPECT_EQ(DetailedF1Properties.CallReturnsVectorPointerCount, 1);
1000 EnableDetailedFunctionProperties.setValue(false);
1003 TEST_F(FunctionPropertiesAnalysisTest, ReAddEdges) {
1004 LLVMContext C;
1005 std::unique_ptr<Module> M = makeLLVMModule(C, R"IR(
1006 define hidden void @f1(ptr noundef %destatep, i32 noundef %offset, i8 noundef zeroext %byte1) {
1007 entry:
1008 %cmp = icmp eq i8 %byte1, 0
1009 br i1 %cmp, label %if.then, label %if.else
1011 if.then: ; preds = %entry
1012 call fastcc void @f2(ptr noundef %destatep, i32 noundef 37, i32 noundef 600)
1013 %and = and i32 %offset, 3
1014 switch i32 %and, label %default.unreachable [
1015 i32 0, label %sw.bb
1016 i32 1, label %sw.bb1
1017 i32 2, label %sw.bb1
1018 i32 3, label %if.end
1021 sw.bb: ; preds = %if.then
1022 call fastcc void @f2(ptr noundef %destatep, i32 noundef 57, i32 noundef 600)
1023 br label %if.end
1025 sw.bb1: ; preds = %if.then, %if.then
1026 call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600) #34
1027 br label %if.end
1029 default.unreachable: ; preds = %if.then
1030 unreachable
1032 if.else: ; preds = %entry
1033 call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600)
1034 br label %if.end
1036 if.end: ; preds = %sw.bb, %sw.bb1, %if.then, %if.else
1037 ret void
1040 define internal fastcc void @f2(ptr nocapture noundef %destatep, i32 noundef %r_enc, i32 noundef %whack) {
1041 entry:
1042 %enc_prob = getelementptr inbounds nuw i8, ptr %destatep, i32 512
1043 %arrayidx = getelementptr inbounds [67 x i32], ptr %enc_prob, i32 0, i32 %r_enc
1044 %0 = load i32, ptr %arrayidx, align 4
1045 %sub = sub nsw i32 %0, %whack
1046 store i32 %sub, ptr %arrayidx, align 4
1047 ret void
1049 )IR");
1050 auto *F1 = M->getFunction("f1");
1051 auto *F2 = M->getFunction("f2");
1052 auto *CB = [&]() -> CallBase * {
1053 for (auto &BB : *F1)
1054 for (auto &I : BB)
1055 if (auto *CB = dyn_cast<CallBase>(&I);
1056 CB && CB->getCalledFunction() && CB->getCalledFunction() == F2)
1057 return CB;
1058 return nullptr;
1059 }();
1060 ASSERT_NE(CB, nullptr);
1061 auto FPI = buildFPI(*F1);
1062 FunctionPropertiesUpdater FPU(FPI, *CB);
1063 InlineFunctionInfo IFI;
1064 auto IR = llvm::InlineFunction(*CB, IFI);
1065 EXPECT_TRUE(IR.isSuccess());
1066 invalidate(*F1);
1067 EXPECT_TRUE(FPU.finishAndTest(FAM));
1070 TEST_F(FunctionPropertiesAnalysisTest, InvokeLandingCanStillBeReached) {
1071 LLVMContext C;
1072 // %lpad is reachable from a block not involved in the inlining decision. We
1073 // make sure that's not the entry - otherwise the DT will be recomputed from
1074 // scratch. The idea here is that the edge known to the inliner to potentially
1075 // disappear - %lpad->%ehcleanup -should survive because it is still reachable
1076 // from %middle.
1077 std::unique_ptr<Module> M = makeLLVMModule(C,
1078 R"IR(
1079 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
1080 target triple = "x86_64-pc-linux-gnu"
1082 define i64 @f1(i32 noundef %value) {
1083 entry:
1084 br label %middle
1085 middle:
1086 %c = icmp eq i32 %value, 0
1087 br i1 %c, label %invoke, label %lpad
1088 invoke:
1089 invoke fastcc void @f2() to label %cont unwind label %lpad
1090 cont:
1091 br label %exit
1092 lpad:
1093 %lp = landingpad i32 cleanup
1094 br label %ehcleanup
1095 ehcleanup:
1096 resume i32 0
1097 exit:
1098 ret i64 1
1100 define void @f2() {
1101 ret void
1103 )IR");
1105 Function *F1 = M->getFunction("f1");
1106 CallBase *CB = findCall(*F1);
1107 EXPECT_NE(CB, nullptr);
1109 auto FPI = buildFPI(*F1);
1110 FunctionPropertiesUpdater FPU(FPI, *CB);
1111 InlineFunctionInfo IFI;
1112 auto IR = llvm::InlineFunction(*CB, IFI);
1113 EXPECT_TRUE(IR.isSuccess());
1114 invalidate(*F1);
1115 EXPECT_TRUE(FPU.finishAndTest(FAM));
1117 } // end anonymous namespace