1 //=========- MemTransferLowerTest.cpp - MemTransferLower unit tests -=========//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Analysis/CGSCCPassManager.h"
10 #include "llvm/Analysis/ScalarEvolution.h"
11 #include "llvm/Analysis/TargetTransformInfo.h"
12 #include "llvm/AsmParser/Parser.h"
13 #include "llvm/IR/BasicBlock.h"
14 #include "llvm/IR/Function.h"
15 #include "llvm/IR/Instructions.h"
16 #include "llvm/IR/IntrinsicInst.h"
17 #include "llvm/IR/LLVMContext.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/IR/PassManager.h"
20 #include "llvm/InitializePasses.h"
21 #include "llvm/Passes/PassBuilder.h"
22 #include "llvm/Support/Debug.h"
23 #include "llvm/Support/SourceMgr.h"
24 #include "llvm/Testing/Support/Error.h"
25 #include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
26 #include "llvm/Transforms/Vectorize/LoopVectorize.h"
28 #include "gtest/gtest-spi.h"
29 #include "gtest/gtest.h"
34 struct ForwardingPass
: public PassInfoMixin
<ForwardingPass
> {
35 template <typename T
> ForwardingPass(T
&&Arg
) : Func(std::forward
<T
>(Arg
)) {}
37 PreservedAnalyses
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
41 std::function
<PreservedAnalyses(Function
&, FunctionAnalysisManager
&)> Func
;
44 struct MemTransferLowerTest
: public testing::Test
{
46 LoopAnalysisManager LAM
;
47 FunctionAnalysisManager FAM
;
48 CGSCCAnalysisManager CGAM
;
49 ModuleAnalysisManager MAM
;
50 ModulePassManager MPM
;
52 std::unique_ptr
<Module
> M
;
54 MemTransferLowerTest() {
55 // Register all the basic analyses with the managers.
56 PB
.registerModuleAnalyses(MAM
);
57 PB
.registerCGSCCAnalyses(CGAM
);
58 PB
.registerFunctionAnalyses(FAM
);
59 PB
.registerLoopAnalyses(LAM
);
60 PB
.crossRegisterProxies(LAM
, FAM
, CGAM
, MAM
);
63 BasicBlock
*getBasicBlockByName(Function
&F
, StringRef Name
) const {
64 for (BasicBlock
&BB
: F
) {
65 if (BB
.getName() == Name
)
71 Instruction
*getInstructionByOpcode(BasicBlock
&BB
, unsigned Opcode
,
72 unsigned Number
) const {
73 unsigned CurrNumber
= 0;
74 for (Instruction
&I
: BB
)
75 if (I
.getOpcode() == Opcode
) {
77 if (CurrNumber
== Number
)
83 void ParseAssembly(const char *IR
) {
85 M
= parseAssemblyString(IR
, Error
, Context
);
87 raw_string_ostream
os(errMsg
);
90 // A failure here means that the test itself is buggy.
92 report_fatal_error(os
.str().c_str());
96 // By semantics source and destination of llvm.memcpy.* intrinsic
97 // are either equal or don't overlap. Once the intrinsic is lowered
98 // to a loop it can be hard or impossible to reason about these facts.
99 // For that reason expandMemCpyAsLoop is expected to explicitly mark
100 // loads from source and stores to destination as not aliasing.
101 TEST_F(MemTransferLowerTest
, MemCpyKnownLength
) {
102 ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n"
103 "define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n"
105 " %is_not_equal = icmp ne i8* %dst, %src\n"
106 " br i1 %is_not_equal, label %memcpy, label %exit\n"
108 " call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
109 "i64 1024, i1 false)\n"
115 FunctionPassManager FPM
;
116 FPM
.addPass(ForwardingPass(
117 [=](Function
&F
, FunctionAnalysisManager
&FAM
) -> PreservedAnalyses
{
118 TargetTransformInfo
TTI(M
->getDataLayout());
119 auto *MemCpyBB
= getBasicBlockByName(F
, "memcpy");
120 Instruction
*Inst
= &MemCpyBB
->front();
121 MemCpyInst
*MemCpyI
= cast
<MemCpyInst
>(Inst
);
122 auto &SE
= FAM
.getResult
<ScalarEvolutionAnalysis
>(F
);
123 expandMemCpyAsLoop(MemCpyI
, TTI
, &SE
);
124 auto *CopyLoopBB
= getBasicBlockByName(F
, "load-store-loop");
125 Instruction
*LoadInst
=
126 getInstructionByOpcode(*CopyLoopBB
, Instruction::Load
, 1);
127 EXPECT_NE(nullptr, LoadInst
->getMetadata(LLVMContext::MD_alias_scope
));
128 Instruction
*StoreInst
=
129 getInstructionByOpcode(*CopyLoopBB
, Instruction::Store
, 1);
130 EXPECT_NE(nullptr, StoreInst
->getMetadata(LLVMContext::MD_noalias
));
131 return PreservedAnalyses::none();
133 MPM
.addPass(createModuleToFunctionPassAdaptor(std::move(FPM
)));
138 // This test indirectly checks that loads and stores (generated as a result of
139 // llvm.memcpy lowering) doesn't alias by making sure the loop can be
140 // successfully vectorized without additional runtime checks.
141 TEST_F(MemTransferLowerTest
, VecMemCpyKnownLength
) {
142 ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n"
143 "define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n"
145 " %is_not_equal = icmp ne i8* %dst, %src\n"
146 " br i1 %is_not_equal, label %memcpy, label %exit\n"
148 " call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
149 "i64 1024, i1 false)\n"
155 FunctionPassManager FPM
;
156 FPM
.addPass(ForwardingPass(
157 [=](Function
&F
, FunctionAnalysisManager
&FAM
) -> PreservedAnalyses
{
158 TargetTransformInfo
TTI(M
->getDataLayout());
159 auto *MemCpyBB
= getBasicBlockByName(F
, "memcpy");
160 Instruction
*Inst
= &MemCpyBB
->front();
161 MemCpyInst
*MemCpyI
= cast
<MemCpyInst
>(Inst
);
162 auto &SE
= FAM
.getResult
<ScalarEvolutionAnalysis
>(F
);
163 expandMemCpyAsLoop(MemCpyI
, TTI
, &SE
);
164 return PreservedAnalyses::none();
166 FPM
.addPass(LoopVectorizePass(LoopVectorizeOptions()));
167 FPM
.addPass(ForwardingPass(
168 [=](Function
&F
, FunctionAnalysisManager
&FAM
) -> PreservedAnalyses
{
169 auto *TargetBB
= getBasicBlockByName(F
, "vector.body");
170 EXPECT_NE(nullptr, TargetBB
);
171 return PreservedAnalyses::all();
173 MPM
.addPass(createModuleToFunctionPassAdaptor(std::move(FPM
)));
178 TEST_F(MemTransferLowerTest
, AtomicMemCpyKnownLength
) {
179 ParseAssembly("declare void "
180 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, "
182 "define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n"
184 " %is_not_equal = icmp ne i32* %dst, %src\n"
185 " br i1 %is_not_equal, label %memcpy, label %exit\n"
188 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* "
196 FunctionPassManager FPM
;
197 FPM
.addPass(ForwardingPass(
198 [=](Function
&F
, FunctionAnalysisManager
&FAM
) -> PreservedAnalyses
{
199 TargetTransformInfo
TTI(M
->getDataLayout());
200 auto *MemCpyBB
= getBasicBlockByName(F
, "memcpy");
201 Instruction
*Inst
= &MemCpyBB
->front();
202 assert(isa
<AtomicMemCpyInst
>(Inst
) &&
203 "Expecting llvm.memcpy.p0i8.i64 instructon");
204 AtomicMemCpyInst
*MemCpyI
= cast
<AtomicMemCpyInst
>(Inst
);
205 auto &SE
= FAM
.getResult
<ScalarEvolutionAnalysis
>(F
);
206 expandAtomicMemCpyAsLoop(MemCpyI
, TTI
, &SE
);
207 auto *CopyLoopBB
= getBasicBlockByName(F
, "load-store-loop");
208 Instruction
*LoadInst
=
209 getInstructionByOpcode(*CopyLoopBB
, Instruction::Load
, 1);
210 EXPECT_TRUE(LoadInst
->isAtomic());
211 EXPECT_NE(LoadInst
->getMetadata(LLVMContext::MD_alias_scope
), nullptr);
212 Instruction
*StoreInst
=
213 getInstructionByOpcode(*CopyLoopBB
, Instruction::Store
, 1);
214 EXPECT_TRUE(StoreInst
->isAtomic());
215 EXPECT_NE(StoreInst
->getMetadata(LLVMContext::MD_noalias
), nullptr);
216 return PreservedAnalyses::none();
218 MPM
.addPass(createModuleToFunctionPassAdaptor(std::move(FPM
)));
223 TEST_F(MemTransferLowerTest
, AtomicMemCpyUnKnownLength
) {
224 ParseAssembly("declare void "
225 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, "
227 "define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n"
229 " %is_not_equal = icmp ne i32* %dst, %src\n"
230 " br i1 %is_not_equal, label %memcpy, label %exit\n"
233 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* "
241 FunctionPassManager FPM
;
242 FPM
.addPass(ForwardingPass(
243 [=](Function
&F
, FunctionAnalysisManager
&FAM
) -> PreservedAnalyses
{
244 TargetTransformInfo
TTI(M
->getDataLayout());
245 auto *MemCpyBB
= getBasicBlockByName(F
, "memcpy");
246 Instruction
*Inst
= &MemCpyBB
->front();
247 assert(isa
<AtomicMemCpyInst
>(Inst
) &&
248 "Expecting llvm.memcpy.p0i8.i64 instructon");
249 AtomicMemCpyInst
*MemCpyI
= cast
<AtomicMemCpyInst
>(Inst
);
250 auto &SE
= FAM
.getResult
<ScalarEvolutionAnalysis
>(F
);
251 expandAtomicMemCpyAsLoop(MemCpyI
, TTI
, &SE
);
252 auto *CopyLoopBB
= getBasicBlockByName(F
, "loop-memcpy-expansion");
253 Instruction
*LoadInst
=
254 getInstructionByOpcode(*CopyLoopBB
, Instruction::Load
, 1);
255 EXPECT_TRUE(LoadInst
->isAtomic());
256 EXPECT_NE(LoadInst
->getMetadata(LLVMContext::MD_alias_scope
), nullptr);
257 Instruction
*StoreInst
=
258 getInstructionByOpcode(*CopyLoopBB
, Instruction::Store
, 1);
259 EXPECT_TRUE(StoreInst
->isAtomic());
260 EXPECT_NE(StoreInst
->getMetadata(LLVMContext::MD_noalias
), nullptr);
261 return PreservedAnalyses::none();
263 MPM
.addPass(createModuleToFunctionPassAdaptor(std::move(FPM
)));