684 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			684 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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/ADT/iterator_range.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 {
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| 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);
 | |
|   FPU.finish(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);
 | |
|   FPU.finish(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);
 | |
|   FPU.finish(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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
 | |
|             F1->getBasicBlockList().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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
 | |
|             F1->getBasicBlockList().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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
 | |
|             F1->getBasicBlockList().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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
 | |
|             F1->getBasicBlockList().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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
 | |
|             F1->getBasicBlockList().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);
 | |
|   FPU.finish(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 %cond.end
 | |
| 
 | |
| cond.end:                                         ; preds = %cond.false, %cond.true
 | |
|   %cond = phi i64 [ %conv2, %cond.true ], [ %call3, %cond.false ]
 | |
|   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 = 4;
 | |
|   ExpectedInitial.TotalInstructionCount = 7;
 | |
|   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);
 | |
|   FPU.finish(FAM);
 | |
|   EXPECT_EQ(FPI, ExpectedFinal);
 | |
| }
 | |
| 
 | |
| } // end anonymous namespace
 |