xref: /llvm-project/llvm/unittests/Transforms/Utils/MemTransferLowering.cpp (revision 52b48a70d3752f9db36ddcfd26d0451c009b19fc)
14661a65fSEvgeniy Brevnov //=========- MemTransferLowerTest.cpp - MemTransferLower unit tests -=========//
24661a65fSEvgeniy Brevnov //
34661a65fSEvgeniy Brevnov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44661a65fSEvgeniy Brevnov // See https://llvm.org/LICENSE.txt for license information.
54661a65fSEvgeniy Brevnov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64661a65fSEvgeniy Brevnov //
74661a65fSEvgeniy Brevnov //===----------------------------------------------------------------------===//
84661a65fSEvgeniy Brevnov 
94661a65fSEvgeniy Brevnov #include "llvm/Analysis/CGSCCPassManager.h"
104661a65fSEvgeniy Brevnov #include "llvm/Analysis/ScalarEvolution.h"
114661a65fSEvgeniy Brevnov #include "llvm/Analysis/TargetTransformInfo.h"
124661a65fSEvgeniy Brevnov #include "llvm/AsmParser/Parser.h"
134661a65fSEvgeniy Brevnov #include "llvm/IR/BasicBlock.h"
144661a65fSEvgeniy Brevnov #include "llvm/IR/Function.h"
154661a65fSEvgeniy Brevnov #include "llvm/IR/Instructions.h"
164661a65fSEvgeniy Brevnov #include "llvm/IR/IntrinsicInst.h"
174661a65fSEvgeniy Brevnov #include "llvm/IR/LLVMContext.h"
184661a65fSEvgeniy Brevnov #include "llvm/IR/Module.h"
194661a65fSEvgeniy Brevnov #include "llvm/IR/PassManager.h"
204661a65fSEvgeniy Brevnov #include "llvm/Passes/PassBuilder.h"
214661a65fSEvgeniy Brevnov #include "llvm/Support/Debug.h"
224661a65fSEvgeniy Brevnov #include "llvm/Support/SourceMgr.h"
234661a65fSEvgeniy Brevnov #include "llvm/Testing/Support/Error.h"
244661a65fSEvgeniy Brevnov #include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
254661a65fSEvgeniy Brevnov #include "llvm/Transforms/Vectorize/LoopVectorize.h"
264661a65fSEvgeniy Brevnov 
274661a65fSEvgeniy Brevnov #include "gtest/gtest-spi.h"
284661a65fSEvgeniy Brevnov #include "gtest/gtest.h"
294661a65fSEvgeniy Brevnov 
304661a65fSEvgeniy Brevnov using namespace llvm;
314661a65fSEvgeniy Brevnov 
324661a65fSEvgeniy Brevnov namespace {
334661a65fSEvgeniy Brevnov struct ForwardingPass : public PassInfoMixin<ForwardingPass> {
344661a65fSEvgeniy Brevnov   template <typename T> ForwardingPass(T &&Arg) : Func(std::forward<T>(Arg)) {}
354661a65fSEvgeniy Brevnov 
364661a65fSEvgeniy Brevnov   PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
374661a65fSEvgeniy Brevnov     return Func(F, FAM);
384661a65fSEvgeniy Brevnov   }
394661a65fSEvgeniy Brevnov 
404661a65fSEvgeniy Brevnov   std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func;
414661a65fSEvgeniy Brevnov };
424661a65fSEvgeniy Brevnov 
434661a65fSEvgeniy Brevnov struct MemTransferLowerTest : public testing::Test {
444661a65fSEvgeniy Brevnov   PassBuilder PB;
454661a65fSEvgeniy Brevnov   LoopAnalysisManager LAM;
464661a65fSEvgeniy Brevnov   FunctionAnalysisManager FAM;
474661a65fSEvgeniy Brevnov   CGSCCAnalysisManager CGAM;
484661a65fSEvgeniy Brevnov   ModuleAnalysisManager MAM;
494661a65fSEvgeniy Brevnov   ModulePassManager MPM;
504661a65fSEvgeniy Brevnov   LLVMContext Context;
514661a65fSEvgeniy Brevnov   std::unique_ptr<Module> M;
524661a65fSEvgeniy Brevnov 
534661a65fSEvgeniy Brevnov   MemTransferLowerTest() {
544661a65fSEvgeniy Brevnov     // Register all the basic analyses with the managers.
554661a65fSEvgeniy Brevnov     PB.registerModuleAnalyses(MAM);
564661a65fSEvgeniy Brevnov     PB.registerCGSCCAnalyses(CGAM);
574661a65fSEvgeniy Brevnov     PB.registerFunctionAnalyses(FAM);
584661a65fSEvgeniy Brevnov     PB.registerLoopAnalyses(LAM);
594661a65fSEvgeniy Brevnov     PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
604661a65fSEvgeniy Brevnov   }
614661a65fSEvgeniy Brevnov 
624661a65fSEvgeniy Brevnov   BasicBlock *getBasicBlockByName(Function &F, StringRef Name) const {
634661a65fSEvgeniy Brevnov     for (BasicBlock &BB : F) {
644661a65fSEvgeniy Brevnov       if (BB.getName() == Name)
654661a65fSEvgeniy Brevnov         return &BB;
664661a65fSEvgeniy Brevnov     }
674661a65fSEvgeniy Brevnov     return nullptr;
684661a65fSEvgeniy Brevnov   }
694661a65fSEvgeniy Brevnov 
704661a65fSEvgeniy Brevnov   Instruction *getInstructionByOpcode(BasicBlock &BB, unsigned Opcode,
714661a65fSEvgeniy Brevnov                                       unsigned Number) const {
724661a65fSEvgeniy Brevnov     unsigned CurrNumber = 0;
734661a65fSEvgeniy Brevnov     for (Instruction &I : BB)
744661a65fSEvgeniy Brevnov       if (I.getOpcode() == Opcode) {
754661a65fSEvgeniy Brevnov         ++CurrNumber;
764661a65fSEvgeniy Brevnov         if (CurrNumber == Number)
774661a65fSEvgeniy Brevnov           return &I;
784661a65fSEvgeniy Brevnov       }
794661a65fSEvgeniy Brevnov     return nullptr;
804661a65fSEvgeniy Brevnov   }
814661a65fSEvgeniy Brevnov 
824661a65fSEvgeniy Brevnov   void ParseAssembly(const char *IR) {
834661a65fSEvgeniy Brevnov     SMDiagnostic Error;
844661a65fSEvgeniy Brevnov     M = parseAssemblyString(IR, Error, Context);
854661a65fSEvgeniy Brevnov     std::string errMsg;
864661a65fSEvgeniy Brevnov     raw_string_ostream os(errMsg);
874661a65fSEvgeniy Brevnov     Error.print("", os);
884661a65fSEvgeniy Brevnov 
894661a65fSEvgeniy Brevnov     // A failure here means that the test itself is buggy.
904661a65fSEvgeniy Brevnov     if (!M)
91*52b48a70SJOE1994       report_fatal_error(errMsg.c_str());
924661a65fSEvgeniy Brevnov   }
934661a65fSEvgeniy Brevnov };
944661a65fSEvgeniy Brevnov 
954661a65fSEvgeniy Brevnov // By semantics source and destination of llvm.memcpy.* intrinsic
964661a65fSEvgeniy Brevnov // are either equal or don't overlap. Once the intrinsic is lowered
974661a65fSEvgeniy Brevnov // to a loop it can be hard or impossible to reason about these facts.
984661a65fSEvgeniy Brevnov // For that reason expandMemCpyAsLoop is expected to  explicitly mark
994661a65fSEvgeniy Brevnov // loads from source and stores to destination as not aliasing.
1004661a65fSEvgeniy Brevnov TEST_F(MemTransferLowerTest, MemCpyKnownLength) {
1014661a65fSEvgeniy Brevnov   ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n"
1024661a65fSEvgeniy Brevnov                 "define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n"
1034661a65fSEvgeniy Brevnov                 "entry:\n"
1044661a65fSEvgeniy Brevnov                 "  %is_not_equal = icmp ne i8* %dst, %src\n"
1054661a65fSEvgeniy Brevnov                 "  br i1 %is_not_equal, label %memcpy, label %exit\n"
1064661a65fSEvgeniy Brevnov                 "memcpy:\n"
1074661a65fSEvgeniy Brevnov                 "  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
1084661a65fSEvgeniy Brevnov                 "i64 1024, i1 false)\n"
1094661a65fSEvgeniy Brevnov                 "  br label %exit\n"
1104661a65fSEvgeniy Brevnov                 "exit:\n"
1114661a65fSEvgeniy Brevnov                 "  ret void\n"
1124661a65fSEvgeniy Brevnov                 "}\n");
1134661a65fSEvgeniy Brevnov 
1144661a65fSEvgeniy Brevnov   FunctionPassManager FPM;
1154661a65fSEvgeniy Brevnov   FPM.addPass(ForwardingPass(
1164661a65fSEvgeniy Brevnov       [=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
1174661a65fSEvgeniy Brevnov         TargetTransformInfo TTI(M->getDataLayout());
1184661a65fSEvgeniy Brevnov         auto *MemCpyBB = getBasicBlockByName(F, "memcpy");
1194661a65fSEvgeniy Brevnov         Instruction *Inst = &MemCpyBB->front();
1204661a65fSEvgeniy Brevnov         MemCpyInst *MemCpyI = cast<MemCpyInst>(Inst);
121acfc785cSEvgeniy Brevnov         auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
122acfc785cSEvgeniy Brevnov         expandMemCpyAsLoop(MemCpyI, TTI, &SE);
1234661a65fSEvgeniy Brevnov         auto *CopyLoopBB = getBasicBlockByName(F, "load-store-loop");
1244661a65fSEvgeniy Brevnov         Instruction *LoadInst =
1254661a65fSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);
126acfc785cSEvgeniy Brevnov         EXPECT_NE(nullptr, LoadInst->getMetadata(LLVMContext::MD_alias_scope));
1274661a65fSEvgeniy Brevnov         Instruction *StoreInst =
1284661a65fSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);
129acfc785cSEvgeniy Brevnov         EXPECT_NE(nullptr, StoreInst->getMetadata(LLVMContext::MD_noalias));
1304661a65fSEvgeniy Brevnov         return PreservedAnalyses::none();
1314661a65fSEvgeniy Brevnov       }));
1324661a65fSEvgeniy Brevnov   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
1334661a65fSEvgeniy Brevnov 
1344661a65fSEvgeniy Brevnov   MPM.run(*M, MAM);
1354661a65fSEvgeniy Brevnov }
1364661a65fSEvgeniy Brevnov 
1374661a65fSEvgeniy Brevnov // This test indirectly checks that loads and stores (generated as a result of
1384661a65fSEvgeniy Brevnov // llvm.memcpy lowering) doesn't alias by making sure the loop can be
1394661a65fSEvgeniy Brevnov // successfully vectorized without additional runtime checks.
1404661a65fSEvgeniy Brevnov TEST_F(MemTransferLowerTest, VecMemCpyKnownLength) {
1414661a65fSEvgeniy Brevnov   ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n"
1424661a65fSEvgeniy Brevnov                 "define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n"
1434661a65fSEvgeniy Brevnov                 "entry:\n"
1444661a65fSEvgeniy Brevnov                 "  %is_not_equal = icmp ne i8* %dst, %src\n"
1454661a65fSEvgeniy Brevnov                 "  br i1 %is_not_equal, label %memcpy, label %exit\n"
1464661a65fSEvgeniy Brevnov                 "memcpy:\n"
1474661a65fSEvgeniy Brevnov                 "  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
1484661a65fSEvgeniy Brevnov                 "i64 1024, i1 false)\n"
1494661a65fSEvgeniy Brevnov                 "  br label %exit\n"
1504661a65fSEvgeniy Brevnov                 "exit:\n"
1514661a65fSEvgeniy Brevnov                 "  ret void\n"
1524661a65fSEvgeniy Brevnov                 "}\n");
1534661a65fSEvgeniy Brevnov 
1544661a65fSEvgeniy Brevnov   FunctionPassManager FPM;
1554661a65fSEvgeniy Brevnov   FPM.addPass(ForwardingPass(
1564661a65fSEvgeniy Brevnov       [=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
1574661a65fSEvgeniy Brevnov         TargetTransformInfo TTI(M->getDataLayout());
1584661a65fSEvgeniy Brevnov         auto *MemCpyBB = getBasicBlockByName(F, "memcpy");
1594661a65fSEvgeniy Brevnov         Instruction *Inst = &MemCpyBB->front();
1604661a65fSEvgeniy Brevnov         MemCpyInst *MemCpyI = cast<MemCpyInst>(Inst);
161acfc785cSEvgeniy Brevnov         auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
162acfc785cSEvgeniy Brevnov         expandMemCpyAsLoop(MemCpyI, TTI, &SE);
1634661a65fSEvgeniy Brevnov         return PreservedAnalyses::none();
1644661a65fSEvgeniy Brevnov       }));
1654661a65fSEvgeniy Brevnov   FPM.addPass(LoopVectorizePass(LoopVectorizeOptions()));
1664661a65fSEvgeniy Brevnov   FPM.addPass(ForwardingPass(
1674661a65fSEvgeniy Brevnov       [=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
1684661a65fSEvgeniy Brevnov         auto *TargetBB = getBasicBlockByName(F, "vector.body");
169acfc785cSEvgeniy Brevnov         EXPECT_NE(nullptr, TargetBB);
1704661a65fSEvgeniy Brevnov         return PreservedAnalyses::all();
1714661a65fSEvgeniy Brevnov       }));
1724661a65fSEvgeniy Brevnov   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
1734661a65fSEvgeniy Brevnov 
1744661a65fSEvgeniy Brevnov   MPM.run(*M, MAM);
1754661a65fSEvgeniy Brevnov }
176da41214dSEvgeniy Brevnov 
177da41214dSEvgeniy Brevnov TEST_F(MemTransferLowerTest, AtomicMemCpyKnownLength) {
178da41214dSEvgeniy Brevnov   ParseAssembly("declare void "
179da41214dSEvgeniy Brevnov                 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, "
180da41214dSEvgeniy Brevnov                 "i32 *, i64, i32)\n"
181da41214dSEvgeniy Brevnov                 "define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n"
182da41214dSEvgeniy Brevnov                 "entry:\n"
183da41214dSEvgeniy Brevnov                 "  %is_not_equal = icmp ne i32* %dst, %src\n"
184da41214dSEvgeniy Brevnov                 "  br i1 %is_not_equal, label %memcpy, label %exit\n"
185da41214dSEvgeniy Brevnov                 "memcpy:\n"
186da41214dSEvgeniy Brevnov                 "  call void "
187da41214dSEvgeniy Brevnov                 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* "
188da41214dSEvgeniy Brevnov                 "%dst, i32* %src, "
189da41214dSEvgeniy Brevnov                 "i64 1024, i32 4)\n"
190da41214dSEvgeniy Brevnov                 "  br label %exit\n"
191da41214dSEvgeniy Brevnov                 "exit:\n"
192da41214dSEvgeniy Brevnov                 "  ret void\n"
193da41214dSEvgeniy Brevnov                 "}\n");
194da41214dSEvgeniy Brevnov 
195da41214dSEvgeniy Brevnov   FunctionPassManager FPM;
196da41214dSEvgeniy Brevnov   FPM.addPass(ForwardingPass(
197da41214dSEvgeniy Brevnov       [=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
198da41214dSEvgeniy Brevnov         TargetTransformInfo TTI(M->getDataLayout());
199da41214dSEvgeniy Brevnov         auto *MemCpyBB = getBasicBlockByName(F, "memcpy");
200da41214dSEvgeniy Brevnov         Instruction *Inst = &MemCpyBB->front();
201da41214dSEvgeniy Brevnov         assert(isa<AtomicMemCpyInst>(Inst) &&
202da41214dSEvgeniy Brevnov                "Expecting llvm.memcpy.p0i8.i64 instructon");
203da41214dSEvgeniy Brevnov         AtomicMemCpyInst *MemCpyI = cast<AtomicMemCpyInst>(Inst);
204da41214dSEvgeniy Brevnov         auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
205da41214dSEvgeniy Brevnov         expandAtomicMemCpyAsLoop(MemCpyI, TTI, &SE);
206da41214dSEvgeniy Brevnov         auto *CopyLoopBB = getBasicBlockByName(F, "load-store-loop");
207da41214dSEvgeniy Brevnov         Instruction *LoadInst =
208da41214dSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);
209da41214dSEvgeniy Brevnov         EXPECT_TRUE(LoadInst->isAtomic());
210da41214dSEvgeniy Brevnov         EXPECT_NE(LoadInst->getMetadata(LLVMContext::MD_alias_scope), nullptr);
211da41214dSEvgeniy Brevnov         Instruction *StoreInst =
212da41214dSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);
213da41214dSEvgeniy Brevnov         EXPECT_TRUE(StoreInst->isAtomic());
214da41214dSEvgeniy Brevnov         EXPECT_NE(StoreInst->getMetadata(LLVMContext::MD_noalias), nullptr);
215da41214dSEvgeniy Brevnov         return PreservedAnalyses::none();
216da41214dSEvgeniy Brevnov       }));
217da41214dSEvgeniy Brevnov   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
218da41214dSEvgeniy Brevnov 
219da41214dSEvgeniy Brevnov   MPM.run(*M, MAM);
220da41214dSEvgeniy Brevnov }
221da41214dSEvgeniy Brevnov 
222da41214dSEvgeniy Brevnov TEST_F(MemTransferLowerTest, AtomicMemCpyUnKnownLength) {
223da41214dSEvgeniy Brevnov   ParseAssembly("declare void "
224da41214dSEvgeniy Brevnov                 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32*, "
225da41214dSEvgeniy Brevnov                 "i32 *, i64, i32)\n"
226da41214dSEvgeniy Brevnov                 "define void @foo(i32* %dst, i32* %src, i64 %n) optsize {\n"
227da41214dSEvgeniy Brevnov                 "entry:\n"
228da41214dSEvgeniy Brevnov                 "  %is_not_equal = icmp ne i32* %dst, %src\n"
229da41214dSEvgeniy Brevnov                 "  br i1 %is_not_equal, label %memcpy, label %exit\n"
230da41214dSEvgeniy Brevnov                 "memcpy:\n"
231da41214dSEvgeniy Brevnov                 "  call void "
232da41214dSEvgeniy Brevnov                 "@llvm.memcpy.element.unordered.atomic.p0i32.p0i32.i64(i32* "
233da41214dSEvgeniy Brevnov                 "%dst, i32* %src, "
234da41214dSEvgeniy Brevnov                 "i64 %n, i32 4)\n"
235da41214dSEvgeniy Brevnov                 "  br label %exit\n"
236da41214dSEvgeniy Brevnov                 "exit:\n"
237da41214dSEvgeniy Brevnov                 "  ret void\n"
238da41214dSEvgeniy Brevnov                 "}\n");
239da41214dSEvgeniy Brevnov 
240da41214dSEvgeniy Brevnov   FunctionPassManager FPM;
241da41214dSEvgeniy Brevnov   FPM.addPass(ForwardingPass(
242da41214dSEvgeniy Brevnov       [=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
243da41214dSEvgeniy Brevnov         TargetTransformInfo TTI(M->getDataLayout());
244da41214dSEvgeniy Brevnov         auto *MemCpyBB = getBasicBlockByName(F, "memcpy");
245da41214dSEvgeniy Brevnov         Instruction *Inst = &MemCpyBB->front();
246da41214dSEvgeniy Brevnov         assert(isa<AtomicMemCpyInst>(Inst) &&
247da41214dSEvgeniy Brevnov                "Expecting llvm.memcpy.p0i8.i64 instructon");
248da41214dSEvgeniy Brevnov         AtomicMemCpyInst *MemCpyI = cast<AtomicMemCpyInst>(Inst);
249da41214dSEvgeniy Brevnov         auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
250da41214dSEvgeniy Brevnov         expandAtomicMemCpyAsLoop(MemCpyI, TTI, &SE);
251da41214dSEvgeniy Brevnov         auto *CopyLoopBB = getBasicBlockByName(F, "loop-memcpy-expansion");
252da41214dSEvgeniy Brevnov         Instruction *LoadInst =
253da41214dSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);
254da41214dSEvgeniy Brevnov         EXPECT_TRUE(LoadInst->isAtomic());
255da41214dSEvgeniy Brevnov         EXPECT_NE(LoadInst->getMetadata(LLVMContext::MD_alias_scope), nullptr);
256da41214dSEvgeniy Brevnov         Instruction *StoreInst =
257da41214dSEvgeniy Brevnov             getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);
258da41214dSEvgeniy Brevnov         EXPECT_TRUE(StoreInst->isAtomic());
259da41214dSEvgeniy Brevnov         EXPECT_NE(StoreInst->getMetadata(LLVMContext::MD_noalias), nullptr);
260da41214dSEvgeniy Brevnov         return PreservedAnalyses::none();
261da41214dSEvgeniy Brevnov       }));
262da41214dSEvgeniy Brevnov   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
263da41214dSEvgeniy Brevnov 
264da41214dSEvgeniy Brevnov   MPM.run(*M, MAM);
265da41214dSEvgeniy Brevnov }
2664661a65fSEvgeniy Brevnov } // namespace
267