749 lines
20 KiB
C++
749 lines
20 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 %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);
|
|
FPU.finish(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);
|
|
FPU.finish(FAM);
|
|
EXPECT_EQ(FPI, ExpectedFinal);
|
|
}
|
|
} // end anonymous namespace
|