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