|  | //===- FunctionPropertiesAnalysisTest.cpp - Function Properties Unit Tests-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/Analysis/FunctionPropertiesAnalysis.h" | 
|  | #include "llvm/Analysis/AliasAnalysis.h" | 
|  | #include "llvm/Analysis/LoopInfo.h" | 
|  | #include "llvm/AsmParser/Parser.h" | 
|  | #include "llvm/IR/Dominators.h" | 
|  | #include "llvm/IR/Instructions.h" | 
|  | #include "llvm/IR/LLVMContext.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/IR/PassManager.h" | 
|  | #include "llvm/Passes/PassBuilder.h" | 
|  | #include "llvm/Passes/StandardInstrumentations.h" | 
|  | #include "llvm/Support/SourceMgr.h" | 
|  | #include "llvm/Transforms/Utils/Cloning.h" | 
|  | #include "gtest/gtest.h" | 
|  | #include <cstring> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace llvm { | 
|  | extern cl::opt<bool> EnableDetailedFunctionProperties; | 
|  | extern cl::opt<bool> BigBasicBlockInstructionThreshold; | 
|  | extern cl::opt<bool> MediumBasicBlockInstrutionThreshold; | 
|  | } // namespace llvm | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class FunctionPropertiesAnalysisTest : public testing::Test { | 
|  | public: | 
|  | FunctionPropertiesAnalysisTest() { | 
|  | FAM.registerPass([&] { return DominatorTreeAnalysis(); }); | 
|  | FAM.registerPass([&] { return LoopAnalysis(); }); | 
|  | FAM.registerPass([&] { return PassInstrumentationAnalysis(); }); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | std::unique_ptr<DominatorTree> DT; | 
|  | std::unique_ptr<LoopInfo> LI; | 
|  | FunctionAnalysisManager FAM; | 
|  |  | 
|  | FunctionPropertiesInfo buildFPI(Function &F) { | 
|  | return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM); | 
|  | } | 
|  |  | 
|  | void invalidate(Function &F) { | 
|  | PreservedAnalyses PA = PreservedAnalyses::none(); | 
|  | FAM.invalidate(F, PA); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) { | 
|  | SMDiagnostic Err; | 
|  | std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); | 
|  | if (!Mod) | 
|  | Err.print("MLAnalysisTests", errs()); | 
|  | return Mod; | 
|  | } | 
|  |  | 
|  | CallBase* findCall(Function& F, const char* Name = nullptr) { | 
|  | for (auto &BB : F) | 
|  | for (auto &I : BB ) | 
|  | if (auto *CB = dyn_cast<CallBase>(&I)) | 
|  | if (!Name || CB->getName() == Name) | 
|  | return CB; | 
|  | return nullptr; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, BasicTest) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  | declare i32 @f1(i32) | 
|  | declare i32 @f2(i32) | 
|  | define i32 @branches(i32) { | 
|  | %cond = icmp slt i32 %0, 3 | 
|  | br i1 %cond, label %then, label %else | 
|  | then: | 
|  | %ret.1 = call i32 @f1(i32 %0) | 
|  | br label %last.block | 
|  | else: | 
|  | %ret.2 = call i32 @f2(i32 %0) | 
|  | br label %last.block | 
|  | last.block: | 
|  | %ret = phi i32 [%ret.1, %then], [%ret.2, %else] | 
|  | ret i32 %ret | 
|  | } | 
|  | define internal i32 @top() { | 
|  | %1 = call i32 @branches(i32 2) | 
|  | %2 = call i32 @f1(i32 %1) | 
|  | ret i32 %2 | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *BranchesFunction = M->getFunction("branches"); | 
|  | FunctionPropertiesInfo BranchesFeatures = buildFPI(*BranchesFunction); | 
|  | EXPECT_EQ(BranchesFeatures.BasicBlockCount, 4); | 
|  | EXPECT_EQ(BranchesFeatures.BlocksReachedFromConditionalInstruction, 2); | 
|  | // 2 Users: top is one. The other is added because @branches is not internal, | 
|  | // so it may have external callers. | 
|  | EXPECT_EQ(BranchesFeatures.Uses, 2); | 
|  | EXPECT_EQ(BranchesFeatures.DirectCallsToDefinedFunctions, 0); | 
|  | EXPECT_EQ(BranchesFeatures.LoadInstCount, 0); | 
|  | EXPECT_EQ(BranchesFeatures.StoreInstCount, 0); | 
|  | EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0); | 
|  | EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0); | 
|  |  | 
|  | Function *TopFunction = M->getFunction("top"); | 
|  | FunctionPropertiesInfo TopFeatures = buildFPI(*TopFunction); | 
|  | EXPECT_EQ(TopFeatures.BasicBlockCount, 1); | 
|  | EXPECT_EQ(TopFeatures.BlocksReachedFromConditionalInstruction, 0); | 
|  | EXPECT_EQ(TopFeatures.Uses, 0); | 
|  | EXPECT_EQ(TopFeatures.DirectCallsToDefinedFunctions, 1); | 
|  | EXPECT_EQ(BranchesFeatures.LoadInstCount, 0); | 
|  | EXPECT_EQ(BranchesFeatures.StoreInstCount, 0); | 
|  | EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0); | 
|  | EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0); | 
|  |  | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedBranchesFeatures = buildFPI(*BranchesFunction); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSingleSuccessor, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoSuccessors, 1); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoSuccessors, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithSinglePredecessor, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithTwoPredecessors, 1); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlocksWithMoreThanTwoPredecessors, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BigBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.MediumBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.SmallBasicBlocks, 4); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CastInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.FloatingPointInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.IntegerInstructionCount, 4); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.ConstantIntOperandCount, 1); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.ConstantFPOperandCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.ConstantOperandCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.InstructionOperandCount, 4); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.BasicBlockOperandCount, 4); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.GlobalValueOperandCount, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.InlineAsmOperandCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.ArgumentOperandCount, 3); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.UnknownOperandCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CriticalEdgeCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.ControlFlowEdgeCount, 4); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.UnconditionalBranchCount, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.IntrinsicCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.DirectCallCount, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.IndirectCallCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CallReturnsIntegerCount, 2); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CallReturnsFloatCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CallReturnsPointerCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CallWithManyArgumentsCount, 0); | 
|  | EXPECT_EQ(DetailedBranchesFeatures.CallWithPointerArgumentCount, 0); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, DifferentPredecessorSuccessorCounts) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | define i64 @f1() { | 
|  | br i1 0, label %br1, label %finally | 
|  | br1: | 
|  | ret i64 0 | 
|  | finally: | 
|  | ret i64 3 | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 2); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 3); | 
|  | EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 3); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 2); | 
|  | EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ControlFlowEdgeCount, 2); | 
|  | EXPECT_EQ(DetailedF1Properties.UnconditionalBranchCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.DirectCallCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  | define i32 @f1(i32 %a) { | 
|  | %b = call i32 @f2(i32 %a) | 
|  | %c = add i32 %b, 2 | 
|  | ret i32 %c | 
|  | } | 
|  |  | 
|  | define i32 @f2(i32 %a) { | 
|  | %b = add i32 %a, 1 | 
|  | ret i32 %b | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase* CB = findCall(*F1, "b"); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 1; | 
|  | ExpectedInitial.TotalInstructionCount = 3; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; | 
|  | ExpectedFinal.DirectCallsToDefinedFunctions = 0; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  |  | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  | define i32 @f1(i32 %a) { | 
|  | entry: | 
|  | %i = icmp slt i32 %a, 0 | 
|  | br i1 %i, label %if.then, label %if.else | 
|  | if.then: | 
|  | %b = call i32 @f2(i32 %a) | 
|  | %c1 = add i32 %b, 2 | 
|  | br label %end | 
|  | if.else: | 
|  | %c2 = add i32 %a, 1 | 
|  | br label %end | 
|  | end: | 
|  | %ret = phi i32 [%c1, %if.then],[%c2, %if.else] | 
|  | ret i32 %ret | 
|  | } | 
|  |  | 
|  | define i32 @f2(i32 %a) { | 
|  | %b = add i32 %a, 1 | 
|  | ret i32 %b | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase* CB = findCall(*F1, "b"); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 4; | 
|  | ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; | 
|  | ExpectedInitial.TotalInstructionCount = 9; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; | 
|  | ExpectedFinal.DirectCallsToDefinedFunctions = 0; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  |  | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  | define i32 @f1(i32 %a) { | 
|  | entry: | 
|  | %i = icmp slt i32 %a, 0 | 
|  | br i1 %i, label %if.then, label %if.else | 
|  | if.then: | 
|  | %b = call i32 @f2(i32 %a) | 
|  | %c1 = add i32 %b, 2 | 
|  | br label %end | 
|  | if.else: | 
|  | %c2 = add i32 %a, 1 | 
|  | br label %end | 
|  | end: | 
|  | %ret = phi i32 [%c1, %if.then],[%c2, %if.else] | 
|  | ret i32 %ret | 
|  | } | 
|  |  | 
|  | define i32 @f2(i32 %a) { | 
|  | entry: | 
|  | br label %loop | 
|  | loop: | 
|  | %indvar = phi i32 [%indvar.next, %loop], [0, %entry] | 
|  | %b = add i32 %a, %indvar | 
|  | %indvar.next = add i32 %indvar, 1 | 
|  | %cond = icmp slt i32 %indvar.next, %a | 
|  | br i1 %cond, label %loop, label %exit | 
|  | exit: | 
|  | ret i32 %b | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase* CB = findCall(*F1, "b"); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 4; | 
|  | ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; | 
|  | ExpectedInitial.TotalInstructionCount = 9; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal; | 
|  | ExpectedFinal.BasicBlockCount = 6; | 
|  | ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; | 
|  | ExpectedFinal.Uses = 1; | 
|  | ExpectedFinal.MaxLoopDepth = 1; | 
|  | ExpectedFinal.TopLevelLoopCount = 1; | 
|  | ExpectedFinal.TotalInstructionCount = 14; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  |  | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  | declare void @might_throw() | 
|  |  | 
|  | define internal void @callee() { | 
|  | entry: | 
|  | call void @might_throw() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { | 
|  | entry: | 
|  | invoke void @callee() | 
|  | to label %cont unwind label %exc | 
|  |  | 
|  | cont: | 
|  | ret i32 0 | 
|  |  | 
|  | exc: | 
|  | %exn = landingpad {i8*, i32} | 
|  | cleanup | 
|  | ret i32 1 | 
|  | } | 
|  |  | 
|  | declare i32 @__gxx_personality_v0(...) | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("caller"); | 
|  | CallBase* CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size()); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), | 
|  | F1->getInstructionCount()); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | declare void @might_throw() | 
|  |  | 
|  | define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { | 
|  | entry: | 
|  | invoke void @might_throw() | 
|  | to label %cont unwind label %exc | 
|  |  | 
|  | cont: | 
|  | ret i32 0 | 
|  |  | 
|  | exc: | 
|  | %exn = landingpad {i8*, i32} | 
|  | cleanup | 
|  | resume { i8*, i32 } %exn | 
|  | } | 
|  |  | 
|  | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { | 
|  | entry: | 
|  | %X = invoke i32 @callee() | 
|  | to label %cont unwind label %Handler | 
|  |  | 
|  | cont: | 
|  | ret i32 %X | 
|  |  | 
|  | Handler: | 
|  | %exn = landingpad {i8*, i32} | 
|  | cleanup | 
|  | ret i32 1 | 
|  | } | 
|  |  | 
|  | declare i32 @__gxx_personality_v0(...) | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("caller"); | 
|  | CallBase* CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), | 
|  | F1->getInstructionCount() - 2); | 
|  | EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, Rethrow) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | declare void @might_throw() | 
|  |  | 
|  | define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 { | 
|  | entry: | 
|  | invoke void @might_throw() | 
|  | to label %cont unwind label %exc | 
|  |  | 
|  | cont: | 
|  | ret i32 0 | 
|  |  | 
|  | exc: | 
|  | %exn = landingpad {i8*, i32} | 
|  | cleanup | 
|  | resume { i8*, i32 } %exn | 
|  | } | 
|  |  | 
|  | define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { | 
|  | entry: | 
|  | %X = invoke i32 @callee() | 
|  | to label %cont unwind label %Handler | 
|  |  | 
|  | cont: | 
|  | ret i32 %X | 
|  |  | 
|  | Handler: | 
|  | %exn = landingpad {i8*, i32} | 
|  | cleanup | 
|  | ret i32 1 | 
|  | } | 
|  |  | 
|  | declare i32 @__gxx_personality_v0(...) | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("caller"); | 
|  | CallBase* CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), | 
|  | F1->getInstructionCount() - 2); | 
|  | EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | declare void @external_func() | 
|  |  | 
|  | @exception_type1 = external global i8 | 
|  | @exception_type2 = external global i8 | 
|  |  | 
|  |  | 
|  | define internal void @inner() personality i8* null { | 
|  | invoke void @external_func() | 
|  | to label %cont unwind label %lpad | 
|  | cont: | 
|  | ret void | 
|  | lpad: | 
|  | %lp = landingpad i32 | 
|  | catch i8* @exception_type1 | 
|  | resume i32 %lp | 
|  | } | 
|  |  | 
|  | define void @outer() personality i8* null { | 
|  | invoke void @inner() | 
|  | to label %cont unwind label %lpad | 
|  | cont: | 
|  | ret void | 
|  | lpad: | 
|  | %lp = landingpad i32 | 
|  | cleanup | 
|  | catch i8* @exception_type2 | 
|  | resume i32 %lp | 
|  | } | 
|  |  | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("outer"); | 
|  | CallBase* CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), | 
|  | F1->getInstructionCount() - 2); | 
|  | EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | declare void @external_func() | 
|  |  | 
|  | @exception_type1 = external global i8 | 
|  | @exception_type2 = external global i8 | 
|  |  | 
|  |  | 
|  | define internal void @inner() personality i8* null { | 
|  | invoke void @external_func() | 
|  | to label %cont unwind label %lpad | 
|  | cont: | 
|  | ret void | 
|  | lpad: | 
|  | %lp = landingpad i32 | 
|  | catch i8* @exception_type1 | 
|  | resume i32 %lp | 
|  | } | 
|  |  | 
|  | define void @outer(i32 %a) personality i8* null { | 
|  | entry: | 
|  | %i = icmp slt i32 %a, 0 | 
|  | br i1 %i, label %if.then, label %cont | 
|  | if.then: | 
|  | invoke void @inner() | 
|  | to label %cont unwind label %lpad | 
|  | cont: | 
|  | ret void | 
|  | lpad: | 
|  | %lp = landingpad i32 | 
|  | cleanup | 
|  | catch i8* @exception_type2 | 
|  | resume i32 %lp | 
|  | } | 
|  |  | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("outer"); | 
|  | CallBase* CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount), F1->size() - 1); | 
|  | EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount), | 
|  | F1->getInstructionCount() - 2); | 
|  | EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, FAM)); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InlineSameLoopBB) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  |  | 
|  | declare i32 @a() | 
|  | declare i32 @b() | 
|  |  | 
|  | define i32 @f1(i32 %a) { | 
|  | entry: | 
|  | br label %loop | 
|  | loop: | 
|  | %i = call i32 @f2(i32 %a) | 
|  | %c = icmp slt i32 %i, %a | 
|  | br i1 %c, label %loop, label %end | 
|  | end: | 
|  | %r = phi i32 [%i, %loop], [%a, %entry] | 
|  | ret i32 %r | 
|  | } | 
|  |  | 
|  | define i32 @f2(i32 %a) { | 
|  | %cnd = icmp slt i32 %a, 0 | 
|  | br i1 %cnd, label %then, label %else | 
|  | then: | 
|  | %r1 = call i32 @a() | 
|  | br label %end | 
|  | else: | 
|  | %r2 = call i32 @b() | 
|  | br label %end | 
|  | end: | 
|  | %r = phi i32 [%r1, %then], [%r2, %else] | 
|  | ret i32 %r | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase *CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 3; | 
|  | ExpectedInitial.TotalInstructionCount = 6; | 
|  | ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  | ExpectedInitial.MaxLoopDepth = 1; | 
|  | ExpectedInitial.TopLevelLoopCount = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; | 
|  | ExpectedFinal.BasicBlockCount = 6; | 
|  | ExpectedFinal.DirectCallsToDefinedFunctions = 0; | 
|  | ExpectedFinal.BlocksReachedFromConditionalInstruction = 4; | 
|  | ExpectedFinal.TotalInstructionCount = 12; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  |  | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, Unreachable) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  |  | 
|  | define i64 @f1(i32 noundef %value) { | 
|  | entry: | 
|  | br i1 true, label %cond.true, label %cond.false | 
|  |  | 
|  | cond.true:                                        ; preds = %entry | 
|  | %conv2 = sext i32 %value to i64 | 
|  | br label %cond.end | 
|  |  | 
|  | cond.false:                                       ; preds = %entry | 
|  | %call3 = call noundef i64 @f2() | 
|  | br label %extra | 
|  |  | 
|  | extra: | 
|  | br label %extra2 | 
|  |  | 
|  | extra2: | 
|  | br label %cond.end | 
|  |  | 
|  | cond.end:                                         ; preds = %cond.false, %cond.true | 
|  | %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %extra ] | 
|  | ret i64 %cond | 
|  | } | 
|  |  | 
|  | define i64 @f2() { | 
|  | entry: | 
|  | tail call void @llvm.trap() | 
|  | unreachable | 
|  | } | 
|  |  | 
|  | declare void @llvm.trap() | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase *CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 6; | 
|  | ExpectedInitial.TotalInstructionCount = 9; | 
|  | ExpectedInitial.BlocksReachedFromConditionalInstruction = 2; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; | 
|  | ExpectedFinal.BasicBlockCount = 4; | 
|  | ExpectedFinal.DirectCallsToDefinedFunctions = 0; | 
|  | ExpectedFinal.TotalInstructionCount = 7; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  |  | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InvokeSkipLP) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  |  | 
|  | define i64 @f1(i32 noundef %value) { | 
|  | entry: | 
|  | invoke fastcc void @f2() to label %cont unwind label %lpad | 
|  | cont: | 
|  | ret i64 1 | 
|  | lpad: | 
|  | %lp = landingpad i32 cleanup | 
|  | br label %ehcleanup | 
|  | ehcleanup: | 
|  | resume i32 0 | 
|  | } | 
|  | define void @f2() { | 
|  | invoke noundef void @f3() to label %exit unwind label %lpad | 
|  | exit: | 
|  | ret void | 
|  | lpad: | 
|  | %lp = landingpad i32 cleanup | 
|  | resume i32 %lp | 
|  | } | 
|  | declare void @f3() | 
|  | )IR"); | 
|  |  | 
|  | // The outcome of inlining will be that lpad becomes unreachable. The landing | 
|  | // pad of the invoke inherited from f2 will land on a new bb which will branch | 
|  | // to a bb containing the body of lpad. | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase *CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedInitial; | 
|  | ExpectedInitial.BasicBlockCount = 4; | 
|  | ExpectedInitial.TotalInstructionCount = 5; | 
|  | ExpectedInitial.BlocksReachedFromConditionalInstruction = 0; | 
|  | ExpectedInitial.Uses = 1; | 
|  | ExpectedInitial.DirectCallsToDefinedFunctions = 1; | 
|  |  | 
|  | FunctionPropertiesInfo ExpectedFinal = ExpectedInitial; | 
|  | ExpectedFinal.BasicBlockCount = 6; | 
|  | ExpectedFinal.DirectCallsToDefinedFunctions = 0; | 
|  | ExpectedFinal.TotalInstructionCount = 8; | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | EXPECT_EQ(FPI, ExpectedInitial); | 
|  |  | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | EXPECT_EQ(FPI, ExpectedFinal); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, DetailedOperandCount) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | @a = global i64 1 | 
|  |  | 
|  | define i64 @f1(i64 %e) { | 
|  | %b = load i64, i64* @a | 
|  | %c = add i64 %b, 2 | 
|  | %d = call i64 asm "mov $1,$0", "=r,r" (i64 %c) | 
|  | %f = add i64 %d, %e | 
|  | ret i64 %f | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSingleSuccessor, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoSuccessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoSuccessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithSinglePredecessor, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithTwoPredecessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlocksWithMoreThanTwoPredecessors, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.BigBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.MediumBasicBlocks, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.SmallBasicBlocks, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CastInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.FloatingPointInstructionCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.IntegerInstructionCount, 4); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantIntOperandCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantFPOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ConstantOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.InstructionOperandCount, 4); | 
|  | EXPECT_EQ(DetailedF1Properties.BasicBlockOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.GlobalValueOperandCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.InlineAsmOperandCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.ArgumentOperandCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.UnknownOperandCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.ControlFlowEdgeCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.UnconditionalBranchCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.DirectCallCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, IntrinsicCount) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | define float @f1(float %a) { | 
|  | %b = call float @llvm.cos.f32(float %a) | 
|  | ret float %b | 
|  | } | 
|  | declare float @llvm.cos.f32(float) | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.DirectCallCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 0); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, FunctionCallMetrics) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | define i64 @f1(i64 %a) { | 
|  | %b = call i64 @f2(i64 %a, i64 %a, i64 %a, i64 %a, i64 %a) | 
|  | %c = call ptr @f3() | 
|  | call void @f4(ptr %c) | 
|  | %d = call float @f5() | 
|  | %e = call i64 %c(i64 %b) | 
|  | ret i64 %b | 
|  | } | 
|  |  | 
|  | declare i64 @f2(i64,i64,i64,i64,i64) | 
|  | declare ptr @f3() | 
|  | declare void @f4(ptr) | 
|  | declare float @f5() | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.IntrinsicCount, 0); | 
|  | EXPECT_EQ(DetailedF1Properties.DirectCallCount, 4); | 
|  | EXPECT_EQ(DetailedF1Properties.IndirectCallCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsIntegerCount, 2); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsFloatCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsPointerCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithManyArgumentsCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallWithPointerArgumentCount, 1); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, CriticalEdge) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | define i64 @f1(i64 %a) { | 
|  | %b = icmp eq i64 %a, 1 | 
|  | br i1 %b, label %TopBlock1, label %TopBlock2 | 
|  | TopBlock1: | 
|  | %c = add i64 %a, 1 | 
|  | %e = icmp eq i64 %c, 2 | 
|  | br i1 %e, label %BottomBlock1, label %BottomBlock2 | 
|  | TopBlock2: | 
|  | %d = add i64 %a, 2 | 
|  | br label %BottomBlock2 | 
|  | BottomBlock1: | 
|  | ret i64 0 | 
|  | BottomBlock2: | 
|  | %f = phi i64 [ %c, %TopBlock1 ], [ %d, %TopBlock2 ] | 
|  | ret i64 %f | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.CriticalEdgeCount, 1); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, FunctionReturnVectors) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | define <4 x i64> @f1(<4 x i64> %a) { | 
|  | %b = call <4 x i64> @f2() | 
|  | %c = call <4 x float> @f3() | 
|  | %d = call <4 x ptr> @f4() | 
|  | ret <4 x i64> %b | 
|  | } | 
|  |  | 
|  | declare <4 x i64> @f2() | 
|  | declare <4 x float> @f3() | 
|  | declare <4 x ptr> @f4() | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | EnableDetailedFunctionProperties.setValue(true); | 
|  | FunctionPropertiesInfo DetailedF1Properties = buildFPI(*F1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsVectorIntCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsVectorFloatCount, 1); | 
|  | EXPECT_EQ(DetailedF1Properties.CallReturnsVectorPointerCount, 1); | 
|  | EnableDetailedFunctionProperties.setValue(false); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, ReAddEdges) { | 
|  | LLVMContext C; | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, R"IR( | 
|  | define hidden void @f1(ptr noundef %destatep, i32 noundef %offset, i8 noundef zeroext %byte1) { | 
|  | entry: | 
|  | %cmp = icmp eq i8 %byte1, 0 | 
|  | br i1 %cmp, label %if.then, label %if.else | 
|  |  | 
|  | if.then:                                          ; preds = %entry | 
|  | call fastcc void @f2(ptr noundef %destatep, i32 noundef 37, i32 noundef 600) | 
|  | %and = and i32 %offset, 3 | 
|  | switch i32 %and, label %default.unreachable [ | 
|  | i32 0, label %sw.bb | 
|  | i32 1, label %sw.bb1 | 
|  | i32 2, label %sw.bb1 | 
|  | i32 3, label %if.end | 
|  | ] | 
|  |  | 
|  | sw.bb:                                            ; preds = %if.then | 
|  | call fastcc void @f2(ptr noundef %destatep, i32 noundef 57, i32 noundef 600) | 
|  | br label %if.end | 
|  |  | 
|  | sw.bb1:                                           ; preds = %if.then, %if.then | 
|  | call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600) #34 | 
|  | br label %if.end | 
|  |  | 
|  | default.unreachable:                              ; preds = %if.then | 
|  | unreachable | 
|  |  | 
|  | if.else:                                          ; preds = %entry | 
|  | call fastcc void @f2(ptr noundef %destatep, i32 noundef 56, i32 noundef 600) | 
|  | br label %if.end | 
|  |  | 
|  | if.end:                                           ; preds = %sw.bb, %sw.bb1, %if.then, %if.else | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define internal fastcc void @f2(ptr nocapture noundef %destatep, i32 noundef %r_enc, i32 noundef %whack) { | 
|  | entry: | 
|  | %enc_prob = getelementptr inbounds nuw i8, ptr %destatep, i32 512 | 
|  | %arrayidx = getelementptr inbounds [67 x i32], ptr %enc_prob, i32 0, i32 %r_enc | 
|  | %0 = load i32, ptr %arrayidx, align 4 | 
|  | %sub = sub nsw i32 %0, %whack | 
|  | store i32 %sub, ptr %arrayidx, align 4 | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | auto *F1 = M->getFunction("f1"); | 
|  | auto *F2 = M->getFunction("f2"); | 
|  | auto *CB = [&]() -> CallBase * { | 
|  | for (auto &BB : *F1) | 
|  | for (auto &I : BB) | 
|  | if (auto *CB = dyn_cast<CallBase>(&I); | 
|  | CB && CB->getCalledFunction() && CB->getCalledFunction() == F2) | 
|  | return CB; | 
|  | return nullptr; | 
|  | }(); | 
|  | ASSERT_NE(CB, nullptr); | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | } | 
|  |  | 
|  | TEST_F(FunctionPropertiesAnalysisTest, InvokeLandingCanStillBeReached) { | 
|  | LLVMContext C; | 
|  | // %lpad is reachable from a block not involved in the inlining decision. We | 
|  | // make sure that's not the entry - otherwise the DT will be recomputed from | 
|  | // scratch. The idea here is that the edge known to the inliner to potentially | 
|  | // disappear - %lpad->%ehcleanup -should survive because it is still reachable | 
|  | // from %middle. | 
|  | std::unique_ptr<Module> M = makeLLVMModule(C, | 
|  | R"IR( | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-pc-linux-gnu" | 
|  |  | 
|  | define i64 @f1(i32 noundef %value) { | 
|  | entry: | 
|  | br label %middle | 
|  | middle: | 
|  | %c = icmp eq i32 %value, 0 | 
|  | br i1 %c, label %invoke, label %lpad | 
|  | invoke: | 
|  | invoke fastcc void @f2() to label %cont unwind label %lpad | 
|  | cont: | 
|  | br label %exit | 
|  | lpad: | 
|  | %lp = landingpad i32 cleanup | 
|  | br label %ehcleanup | 
|  | ehcleanup: | 
|  | resume i32 0 | 
|  | exit: | 
|  | ret i64 1 | 
|  | } | 
|  | define void @f2() { | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  |  | 
|  | Function *F1 = M->getFunction("f1"); | 
|  | CallBase *CB = findCall(*F1); | 
|  | EXPECT_NE(CB, nullptr); | 
|  |  | 
|  | auto FPI = buildFPI(*F1); | 
|  | FunctionPropertiesUpdater FPU(FPI, *CB); | 
|  | InlineFunctionInfo IFI; | 
|  | auto IR = llvm::InlineFunction(*CB, IFI); | 
|  | EXPECT_TRUE(IR.isSuccess()); | 
|  | invalidate(*F1); | 
|  | EXPECT_TRUE(FPU.finishAndTest(FAM)); | 
|  | } | 
|  | } // end anonymous namespace |