| //===- 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 |