mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 22:00:10 +00:00
[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
This commit is contained in:
parent
644ef58073
commit
be9889b350
@ -282,7 +282,6 @@ instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
|
||||
// clobbers where they don't really exist at all. Please see D43269 for
|
||||
// context.
|
||||
switch (II->getIntrinsicID()) {
|
||||
case Intrinsic::lifetime_end:
|
||||
case Intrinsic::invariant_start:
|
||||
case Intrinsic::invariant_end:
|
||||
case Intrinsic::assume:
|
||||
@ -359,22 +358,6 @@ struct UpwardsMemoryQuery {
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static bool lifetimeEndsAt(MemoryDef *MD, const MemoryLocation &Loc,
|
||||
BatchAAResults &AA) {
|
||||
Instruction *Inst = MD->getMemoryInst();
|
||||
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
case Intrinsic::lifetime_end: {
|
||||
MemoryLocation ArgLoc = MemoryLocation::getAfter(II->getArgOperand(1));
|
||||
return AA.alias(ArgLoc, Loc) == MustAlias;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename AliasAnalysisType>
|
||||
static bool isUseTriviallyOptimizableToLiveOnEntry(AliasAnalysisType &AA,
|
||||
const Instruction *I) {
|
||||
@ -1466,15 +1449,6 @@ void MemorySSA::OptimizeUses::optimizeUsesInBlock(
|
||||
}
|
||||
|
||||
MemoryDef *MD = cast<MemoryDef>(VersionStack[UpperBound]);
|
||||
// If the lifetime of the pointer ends at this instruction, it's live on
|
||||
// entry.
|
||||
if (!UseMLOC.IsCall && lifetimeEndsAt(MD, UseMLOC.getLoc(), *AA)) {
|
||||
// Reset UpperBound to liveOnEntryDef's place in the stack
|
||||
UpperBound = 0;
|
||||
FoundClobberResult = true;
|
||||
LocInfo.AR = MustAlias;
|
||||
break;
|
||||
}
|
||||
ClobberAlias CA = instructionClobbersQuery(MD, MU, UseMLOC, *AA);
|
||||
if (CA.IsClobber) {
|
||||
FoundClobberResult = true;
|
||||
|
@ -1,8 +1,7 @@
|
||||
; RUN: opt -basic-aa -print-memoryssa -verify-memoryssa -enable-new-pm=0 -analyze < %s 2>&1 | FileCheck %s
|
||||
; RUN: opt -aa-pipeline=basic-aa -passes='print<memoryssa>,verify<memoryssa>' -disable-output < %s 2>&1 | FileCheck %s
|
||||
; This test checks a number of things:
|
||||
; First, the lifetime markers should not clobber any uses of Q or P.
|
||||
; Second, the loads of P are MemoryUse(LiveOnEntry) due to the placement of the markers vs the loads.
|
||||
; This test checks that lifetime markers are considered clobbers of %P,
|
||||
; and due to lack of noalias information, of %Q as well.
|
||||
|
||||
define i8 @test(i8* %P, i8* %Q) {
|
||||
entry:
|
||||
@ -18,10 +17,10 @@ entry:
|
||||
; CHECK: 3 = MemoryDef(2)
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 32, i8* %P)
|
||||
call void @llvm.lifetime.end.p0i8(i64 32, i8* %P)
|
||||
; CHECK: MemoryUse(liveOnEntry)
|
||||
; CHECK: MemoryUse(3)
|
||||
; CHECK-NEXT: %1 = load i8, i8* %P
|
||||
%1 = load i8, i8* %P
|
||||
; CHECK: MemoryUse(2)
|
||||
; CHECK: MemoryUse(3)
|
||||
; CHECK-NEXT: %2 = load i8, i8* %Q
|
||||
%2 = load i8, i8* %Q
|
||||
ret i8 %1
|
||||
|
@ -1,6 +1,6 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=0 | FileCheck %s --check-prefixes=CHECK,NO_MSSA
|
||||
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=1 -verify-memoryssa | FileCheck %s --check-prefixes=CHECK,MSSA
|
||||
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=0 | FileCheck %s
|
||||
; RUN: opt < %s -O2 -S -enable-memcpyopt-memoryssa=1 -verify-memoryssa | FileCheck %s
|
||||
|
||||
; performCallSlotOptzn in MemCpy should not exchange the calls to
|
||||
; @llvm.lifetime.start and @llvm.memcpy.
|
||||
@ -27,29 +27,17 @@ bb:
|
||||
ret void
|
||||
}
|
||||
|
||||
; FIXME: Miscompile.
|
||||
define void @memcpy_memcpy_across_lifetime(i8* noalias %p1, i8* noalias %p2, i8* noalias %p3) {
|
||||
; NO_MSSA-LABEL: @memcpy_memcpy_across_lifetime(
|
||||
; NO_MSSA-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
|
||||
; NO_MSSA-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
|
||||
; NO_MSSA-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; 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)
|
||||
; 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)
|
||||
; NO_MSSA-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; NO_MSSA-NEXT: ret void
|
||||
;
|
||||
; MSSA-LABEL: @memcpy_memcpy_across_lifetime(
|
||||
; MSSA-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
|
||||
; MSSA-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
|
||||
; MSSA-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; 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)
|
||||
; 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)
|
||||
; MSSA-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; MSSA-NEXT: ret void
|
||||
; CHECK-LABEL: @memcpy_memcpy_across_lifetime(
|
||||
; CHECK-NEXT: [[A:%.*]] = alloca [16 x i8], align 1
|
||||
; CHECK-NEXT: [[A8:%.*]] = getelementptr inbounds [16 x i8], [16 x i8]* [[A]], i64 0, i64 0
|
||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; 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)
|
||||
; 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)
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull [[A8]])
|
||||
; 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)
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%a = alloca [16 x i8]
|
||||
%a8 = bitcast [16 x i8]* %a to i8*
|
||||
|
Loading…
Reference in New Issue
Block a user