Files
clang-p2996/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp
Mircea Trofin b8c39eb275 Fix FunctionPropertiesAnalysis updating callsite in 1-BB loop
If the callsite is in a single BB loop, we need to exclude the BB from
the successor set (in which it'd be a member), because that set forms a
boundary at which we stop traversing the CFG, when re-ingesting BBs
after inlining; but after inlining, the callsite BB's new successors
should be visited.

Reviewed By: kazu

Differential Revision: https://reviews.llvm.org/D127178
2022-06-08 14:32:00 -07:00

614 lines
16 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/Support/SourceMgr.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "gtest/gtest.h"
#include <cstring>
using namespace llvm;
namespace {
class FunctionPropertiesAnalysisTest : public testing::Test {
protected:
std::unique_ptr<DominatorTree> DT;
std::unique_ptr<LoopInfo> LI;
FunctionPropertiesInfo buildFPI(Function &F) {
DT.reset(new DominatorTree(F));
LI.reset(new LoopInfo(*DT));
return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, *LI);
}
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());
FPU.finish(*LI);
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());
FPU.finish(*LI);
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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, LINew));
}
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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, LINew));
}
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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, LINew));
}
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());
DominatorTree DTNew(*F1);
LoopInfo LINew(DTNew);
FPU.finish(LINew);
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, LINew));
}
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());
FPU.finish(*LI);
EXPECT_EQ(FPI, ExpectedFinal);
}
} // end anonymous namespace