Skip to content

Commit 5adc94b

Browse files
author
Evgeniy Brevnov
committed
New regression test against expandMemCpyAsLoop utility
Unit test for functionality going to be added by D118441 Differential Revision: https://reviews.llvm.org/D118440
1 parent 18b38ff commit 5adc94b

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

llvm/unittests/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_llvm_unittest(UtilsTests
2020
LocalTest.cpp
2121
LoopRotationUtilsTest.cpp
2222
LoopUtilsTest.cpp
23+
MemTransferLowering.cpp
2324
ModuleUtilsTest.cpp
2425
ScalarEvolutionExpanderTest.cpp
2526
SizeOptsTest.cpp
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//=========- MemTransferLowerTest.cpp - MemTransferLower unit tests -=========//
2+
//
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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
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"
27+
28+
#include "gtest/gtest-spi.h"
29+
#include "gtest/gtest.h"
30+
31+
using namespace llvm;
32+
33+
namespace {
34+
struct ForwardingPass : public PassInfoMixin<ForwardingPass> {
35+
template <typename T> ForwardingPass(T &&Arg) : Func(std::forward<T>(Arg)) {}
36+
37+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
38+
return Func(F, FAM);
39+
}
40+
41+
std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func;
42+
};
43+
44+
struct MemTransferLowerTest : public testing::Test {
45+
PassBuilder PB;
46+
LoopAnalysisManager LAM;
47+
FunctionAnalysisManager FAM;
48+
CGSCCAnalysisManager CGAM;
49+
ModuleAnalysisManager MAM;
50+
ModulePassManager MPM;
51+
LLVMContext Context;
52+
std::unique_ptr<Module> M;
53+
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);
61+
}
62+
63+
BasicBlock *getBasicBlockByName(Function &F, StringRef Name) const {
64+
for (BasicBlock &BB : F) {
65+
if (BB.getName() == Name)
66+
return &BB;
67+
}
68+
return nullptr;
69+
}
70+
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) {
76+
++CurrNumber;
77+
if (CurrNumber == Number)
78+
return &I;
79+
}
80+
return nullptr;
81+
}
82+
83+
void ParseAssembly(const char *IR) {
84+
SMDiagnostic Error;
85+
M = parseAssemblyString(IR, Error, Context);
86+
std::string errMsg;
87+
raw_string_ostream os(errMsg);
88+
Error.print("", os);
89+
90+
// A failure here means that the test itself is buggy.
91+
if (!M)
92+
report_fatal_error(os.str().c_str());
93+
}
94+
};
95+
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"
104+
"entry:\n"
105+
" %is_not_equal = icmp ne i8* %dst, %src\n"
106+
" br i1 %is_not_equal, label %memcpy, label %exit\n"
107+
"memcpy:\n"
108+
" call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
109+
"i64 1024, i1 false)\n"
110+
" br label %exit\n"
111+
"exit:\n"
112+
" ret void\n"
113+
"}\n");
114+
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+
expandMemCpyAsLoop(MemCpyI, TTI);
123+
auto *CopyLoopBB = getBasicBlockByName(F, "load-store-loop");
124+
Instruction *LoadInst =
125+
getInstructionByOpcode(*CopyLoopBB, Instruction::Load, 1);
126+
EXPECT_NONFATAL_FAILURE(
127+
EXPECT_NE(LoadInst->getMetadata(LLVMContext::MD_alias_scope),
128+
nullptr),
129+
"");
130+
Instruction *StoreInst =
131+
getInstructionByOpcode(*CopyLoopBB, Instruction::Store, 1);
132+
EXPECT_NONFATAL_FAILURE(
133+
EXPECT_NE(StoreInst->getMetadata(LLVMContext::MD_noalias), nullptr),
134+
"");
135+
return PreservedAnalyses::none();
136+
}));
137+
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
138+
139+
MPM.run(*M, MAM);
140+
}
141+
142+
// This test indirectly checks that loads and stores (generated as a result of
143+
// llvm.memcpy lowering) doesn't alias by making sure the loop can be
144+
// successfully vectorized without additional runtime checks.
145+
TEST_F(MemTransferLowerTest, VecMemCpyKnownLength) {
146+
ParseAssembly("declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8 *, i64, i1)\n"
147+
"define void @foo(i8* %dst, i8* %src, i64 %n) optsize {\n"
148+
"entry:\n"
149+
" %is_not_equal = icmp ne i8* %dst, %src\n"
150+
" br i1 %is_not_equal, label %memcpy, label %exit\n"
151+
"memcpy:\n"
152+
" call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, "
153+
"i64 1024, i1 false)\n"
154+
" br label %exit\n"
155+
"exit:\n"
156+
" ret void\n"
157+
"}\n");
158+
159+
FunctionPassManager FPM;
160+
FPM.addPass(ForwardingPass(
161+
[=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
162+
TargetTransformInfo TTI(M->getDataLayout());
163+
auto *MemCpyBB = getBasicBlockByName(F, "memcpy");
164+
Instruction *Inst = &MemCpyBB->front();
165+
MemCpyInst *MemCpyI = cast<MemCpyInst>(Inst);
166+
expandMemCpyAsLoop(MemCpyI, TTI);
167+
return PreservedAnalyses::none();
168+
}));
169+
FPM.addPass(LoopVectorizePass(LoopVectorizeOptions()));
170+
FPM.addPass(ForwardingPass(
171+
[=](Function &F, FunctionAnalysisManager &FAM) -> PreservedAnalyses {
172+
auto *TargetBB = getBasicBlockByName(F, "vector.body");
173+
EXPECT_NONFATAL_FAILURE(EXPECT_NE(TargetBB, nullptr), "");
174+
return PreservedAnalyses::all();
175+
}));
176+
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
177+
178+
MPM.run(*M, MAM);
179+
}
180+
} // namespace

0 commit comments

Comments
 (0)