Skip to content

Commit dd5991c

Browse files
committed
[LoopIdiom] Transform loop containing memcpy to memmove
The purpose of patch is to learn Loop Idiom Recognize pass how to recognize simple memmove patterns in similar way like GCC does: https://godbolt.org/z/dKjGvTGff It's follow-up of following change: https://reviews.llvm.org/D104464 Differential Revision: https://reviews.llvm.org/D107075
1 parent 00c0ce0 commit dd5991c

File tree

2 files changed

+267
-40
lines changed

2 files changed

+267
-40
lines changed

llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,51 @@ bool LoopIdiomRecognize::processLoopStoreOfLoopLoad(StoreInst *SI,
12571257
StoreEv, LoadEv, BECount);
12581258
}
12591259

1260+
class MemmoveVerifier {
1261+
public:
1262+
explicit MemmoveVerifier(const Value &LoadBasePtr, const Value &StoreBasePtr,
1263+
const DataLayout &DL)
1264+
: DL(DL), LoadOff(0), StoreOff(0),
1265+
BP1(llvm::GetPointerBaseWithConstantOffset(
1266+
LoadBasePtr.stripPointerCasts(), LoadOff, DL)),
1267+
BP2(llvm::GetPointerBaseWithConstantOffset(
1268+
StoreBasePtr.stripPointerCasts(), StoreOff, DL)),
1269+
IsSameObject(BP1 == BP2) {}
1270+
1271+
bool loadAndStoreMayFormMemmove(unsigned StoreSize, bool IsNegStride,
1272+
const Instruction &TheLoad,
1273+
bool IsMemCpy) const {
1274+
if (IsMemCpy) {
1275+
// Ensure that LoadBasePtr is after StoreBasePtr or before StoreBasePtr
1276+
// for negative stride.
1277+
if ((!IsNegStride && LoadOff <= StoreOff) ||
1278+
(IsNegStride && LoadOff >= StoreOff))
1279+
return false;
1280+
} else {
1281+
// Ensure that LoadBasePtr is after StoreBasePtr or before StoreBasePtr
1282+
// for negative stride. LoadBasePtr shouldn't overlap with StoreBasePtr.
1283+
int64_t LoadSize =
1284+
DL.getTypeSizeInBits(TheLoad.getType()).getFixedSize() / 8;
1285+
if (BP1 != BP2 || LoadSize != int64_t(StoreSize))
1286+
return false;
1287+
if ((!IsNegStride && LoadOff < StoreOff + int64_t(StoreSize)) ||
1288+
(IsNegStride && LoadOff + LoadSize > StoreOff))
1289+
return false;
1290+
}
1291+
return true;
1292+
}
1293+
1294+
private:
1295+
const DataLayout &DL;
1296+
int64_t LoadOff;
1297+
int64_t StoreOff;
1298+
const Value *BP1;
1299+
const Value *BP2;
1300+
1301+
public:
1302+
const bool IsSameObject;
1303+
};
1304+
12601305
bool LoopIdiomRecognize::processLoopStoreOfLoopLoad(
12611306
Value *DestPtr, Value *SourcePtr, const SCEV *StoreSizeSCEV,
12621307
MaybeAlign StoreAlign, MaybeAlign LoadAlign, Instruction *TheStore,
@@ -1321,10 +1366,10 @@ bool LoopIdiomRecognize::processLoopStoreOfLoopLoad(
13211366
bool IsMemCpy = isa<MemCpyInst>(TheStore);
13221367
const StringRef InstRemark = IsMemCpy ? "memcpy" : "load and store";
13231368

1324-
bool UseMemMove =
1369+
bool LoopAccessStore =
13251370
mayLoopAccessLocation(StoreBasePtr, ModRefInfo::ModRef, CurLoop, BECount,
13261371
StoreSizeSCEV, *AA, IgnoredInsts);
1327-
if (UseMemMove) {
1372+
if (LoopAccessStore) {
13281373
// For memmove case it's not enough to guarantee that loop doesn't access
13291374
// TheStore and TheLoad. Additionally we need to make sure that TheStore is
13301375
// the only user of TheLoad.
@@ -1363,34 +1408,32 @@ bool LoopIdiomRecognize::processLoopStoreOfLoopLoad(
13631408
// the load memory locations. So remove it from the ignored stores.
13641409
if (IsMemCpy)
13651410
IgnoredInsts.erase(TheStore);
1411+
MemmoveVerifier Verifier(*LoadBasePtr, *StoreBasePtr, *DL);
13661412
if (mayLoopAccessLocation(LoadBasePtr, ModRefInfo::Mod, CurLoop, BECount,
13671413
StoreSizeSCEV, *AA, IgnoredInsts)) {
1368-
ORE.emit([&]() {
1369-
return OptimizationRemarkMissed(DEBUG_TYPE, "LoopMayAccessLoad", TheLoad)
1370-
<< ore::NV("Inst", InstRemark) << " in "
1371-
<< ore::NV("Function", TheStore->getFunction())
1372-
<< " function will not be hoisted: "
1373-
<< ore::NV("Reason", "The loop may access load location");
1374-
});
1375-
return Changed;
1376-
}
1377-
if (UseMemMove) {
1378-
// Ensure that LoadBasePtr is after StoreBasePtr or before StoreBasePtr for
1379-
// negative stride. LoadBasePtr shouldn't overlap with StoreBasePtr.
1380-
int64_t LoadOff = 0, StoreOff = 0;
1381-
const Value *BP1 = llvm::GetPointerBaseWithConstantOffset(
1382-
LoadBasePtr->stripPointerCasts(), LoadOff, *DL);
1383-
const Value *BP2 = llvm::GetPointerBaseWithConstantOffset(
1384-
StoreBasePtr->stripPointerCasts(), StoreOff, *DL);
1385-
int64_t LoadSize =
1386-
DL->getTypeSizeInBits(TheLoad->getType()).getFixedSize() / 8;
1387-
if (BP1 != BP2 || LoadSize != int64_t(StoreSize))
1414+
if (!IsMemCpy) {
1415+
ORE.emit([&]() {
1416+
return OptimizationRemarkMissed(DEBUG_TYPE, "LoopMayAccessLoad",
1417+
TheLoad)
1418+
<< ore::NV("Inst", InstRemark) << " in "
1419+
<< ore::NV("Function", TheStore->getFunction())
1420+
<< " function will not be hoisted: "
1421+
<< ore::NV("Reason", "The loop may access load location");
1422+
});
13881423
return Changed;
1389-
if ((!IsNegStride && LoadOff < StoreOff + int64_t(StoreSize)) ||
1390-
(IsNegStride && LoadOff + LoadSize > StoreOff))
1424+
}
1425+
// At this point loop may access load only for memcpy in same underlying
1426+
// object. If that's not the case bail out.
1427+
if (!Verifier.IsSameObject)
13911428
return Changed;
13921429
}
13931430

1431+
bool UseMemMove = IsMemCpy ? Verifier.IsSameObject : LoopAccessStore;
1432+
if (UseMemMove)
1433+
if (!Verifier.loadAndStoreMayFormMemmove(StoreSize, IsNegStride, *TheLoad,
1434+
IsMemCpy))
1435+
return Changed;
1436+
13941437
if (avoidLIRForMultiBlockLoop())
13951438
return Changed;
13961439

0 commit comments

Comments
 (0)