Skip to content

Commit be9889b

Browse files
committed
[MemorySSA] Don't treat lifetime.end as NoAlias
MemorySSA currently treats lifetime.end intrinsics as not aliasing anything. This breaks MemorySSA-based MemCpyOpt, because we'll happily move a read of a pointer below a lifetime.end intrinsic, as no clobber is reported. I think the MemorySSA modelling here isn't correct: lifetime.end(p) has approximately the same effect as doing a memcpy(p, undef), and should be treated as a clobber. This patch removes the special handling of lifetime.end, leaving alias analysis to handle it appropriately. Differential Revision: https://reviews.llvm.org/D95763
1 parent 644ef58 commit be9889b

File tree

3 files changed

+16
-55
lines changed

3 files changed

+16
-55
lines changed

llvm/lib/Analysis/MemorySSA.cpp

-26
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
282282
// clobbers where they don't really exist at all. Please see D43269 for
283283
// context.
284284
switch (II->getIntrinsicID()) {
285-
case Intrinsic::lifetime_end:
286285
case Intrinsic::invariant_start:
287286
case Intrinsic::invariant_end:
288287
case Intrinsic::assume:
@@ -359,22 +358,6 @@ struct UpwardsMemoryQuery {
359358

360359
} // end anonymous namespace
361360

362-
static bool lifetimeEndsAt(MemoryDef *MD, const MemoryLocation &Loc,
363-
BatchAAResults &AA) {
364-
Instruction *Inst = MD->getMemoryInst();
365-
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) {
366-
switch (II->getIntrinsicID()) {
367-
case Intrinsic::lifetime_end: {
368-
MemoryLocation ArgLoc = MemoryLocation::getAfter(II->getArgOperand(1));
369-
return AA.alias(ArgLoc, Loc) == MustAlias;
370-
}
371-
default:
372-
return false;
373-
}
374-
}
375-
return false;
376-
}
377-
378361
template <typename AliasAnalysisType>
379362
static bool isUseTriviallyOptimizableToLiveOnEntry(AliasAnalysisType &AA,
380363
const Instruction *I) {
@@ -1466,15 +1449,6 @@ void MemorySSA::OptimizeUses::optimizeUsesInBlock(
14661449
}
14671450

14681451
MemoryDef *MD = cast<MemoryDef>(VersionStack[UpperBound]);
1469-
// If the lifetime of the pointer ends at this instruction, it's live on
1470-
// entry.
1471-
if (!UseMLOC.IsCall && lifetimeEndsAt(MD, UseMLOC.getLoc(), *AA)) {
1472-
// Reset UpperBound to liveOnEntryDef's place in the stack
1473-
UpperBound = 0;
1474-
FoundClobberResult = true;
1475-
LocInfo.AR = MustAlias;
1476-
break;
1477-
}
14781452
ClobberAlias CA = instructionClobbersQuery(MD, MU, UseMLOC, *AA);
14791453
if (CA.IsClobber) {
14801454
FoundClobberResult = true;

llvm/test/Analysis/MemorySSA/lifetime-simple.ll

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
; RUN: opt -basic-aa -print-memoryssa -verify-memoryssa -enable-new-pm=0 -analyze < %s 2>&1 | FileCheck %s
22
; RUN: opt -aa-pipeline=basic-aa -passes='print<memoryssa>,verify<memoryssa>' -disable-output < %s 2>&1 | FileCheck %s
3-
; This test checks a number of things:
4-
; First, the lifetime markers should not clobber any uses of Q or P.
5-
; Second, the loads of P are MemoryUse(LiveOnEntry) due to the placement of the markers vs the loads.
3+
; This test checks that lifetime markers are considered clobbers of %P,
4+
; and due to lack of noalias information, of %Q as well.
65

76
define i8 @test(i8* %P, i8* %Q) {
87
entry:
@@ -18,10 +17,10 @@ entry:
1817
; CHECK: 3 = MemoryDef(2)
1918
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 32, i8* %P)
2019
call void @llvm.lifetime.end.p0i8(i64 32, i8* %P)
21-
; CHECK: MemoryUse(liveOnEntry)
20+
; CHECK: MemoryUse(3)
2221
; CHECK-NEXT: %1 = load i8, i8* %P
2322
%1 = load i8, i8* %P
24-
; CHECK: MemoryUse(2)
23+
; CHECK: MemoryUse(3)
2524
; CHECK-NEXT: %2 = load i8, i8* %Q
2625
%2 = load i8, i8* %Q
2726
ret i8 %1

llvm/test/Transforms/MemCpyOpt/lifetime.ll

+12-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2-
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=0 | FileCheck %s --check-prefixes=CHECK,NO_MSSA
3-
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=1 -verify-memoryssa | FileCheck %s --check-prefixes=CHECK,MSSA
2+
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=0 | FileCheck %s
3+
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=1 -verify-memoryssa | FileCheck %s
44

55
; performCallSlotOptzn in MemCpy should not exchange the calls to
66
; @llvm.lifetime.start and @llvm.memcpy.
@@ -27,29 +27,17 @@ bb:
2727
ret void
2828
}
2929

30-
; FIXME: Miscompile.
3130
define void @memcpy_memcpy_across_lifetime(i8* noalias %p1, i8* noalias %p2, i8* noalias %p3) {
32-
; NO_MSSA-LABEL: @memcpy_memcpy_across_lifetime(
33-
; NO_MSSA-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
34-
; NO_MSSA-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
35-
; NO_MSSA-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
36-
; NO_MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[A8]], i8* nonnull align 1 dereferenceable(16) [[P1:%.*]], i64 16, i1 false)
37-
; NO_MSSA-NEXT: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P1]], i8* nonnull align 1 dereferenceable(16) [[P2:%.*]], i64 16, i1 false)
38-
; NO_MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P2]], i8* nonnull align 1 dereferenceable(16) [[A8]], i64 16, i1 false)
39-
; NO_MSSA-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
40-
; NO_MSSA-NEXT: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P3:%.*]], i8* nonnull align 1 dereferenceable(16) [[P2]], i64 16, i1 false)
41-
; NO_MSSA-NEXT: ret void
42-
;
43-
; MSSA-LABEL: @memcpy_memcpy_across_lifetime(
44-
; MSSA-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
45-
; MSSA-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
46-
; MSSA-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
47-
; MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[A8]], i8* nonnull align 1 dereferenceable(16) [[P1:%.*]], i64 16, i1 false)
48-
; MSSA-NEXT: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P1]], i8* nonnull align 1 dereferenceable(16) [[P2:%.*]], i64 16, i1 false)
49-
; MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P2]], i8* nonnull align 1 dereferenceable(16) [[A8]], i64 16, i1 false)
50-
; MSSA-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
51-
; MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P3:%.*]], i8* nonnull align 1 dereferenceable(16) [[A8]], i64 16, i1 false)
52-
; MSSA-NEXT: ret void
31+
; CHECK-LABEL: @memcpy_memcpy_across_lifetime(
32+
; CHECK-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
33+
; CHECK-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
34+
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
35+
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[A8]], i8* nonnull align 1 dereferenceable(16) [[P1:%.*]], i64 16, i1 false)
36+
; CHECK-NEXT: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P1]], i8* nonnull align 1 dereferenceable(16) [[P2:%.*]], i64 16, i1 false)
37+
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P2]], i8* nonnull align 1 dereferenceable(16) [[A8]], i64 16, i1 false)
38+
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
39+
; CHECK-NEXT: tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(16) [[P3:%.*]], i8* nonnull align 1 dereferenceable(16) [[P2]], i64 16, i1 false)
40+
; CHECK-NEXT: ret void
5341
;
5442
%a = alloca [16 x i8]
5543
%a8 = bitcast [16 x i8]* %a to i8*

0 commit comments

Comments
 (0)