diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -822,6 +822,12 @@ return false; } +static bool isLifetimeEnd(const Instruction *Inst) { + if (const IntrinsicInst *II = dyn_cast(Inst)) + return II->getIntrinsicID() == Intrinsic::lifetime_end; + return false; +} + /// Try to locate the three instruction involved in a missed /// load-elimination case that is due to an intervening store. static void reportMayClobberedLoad(LoadInst *LI, MemDepResult DepInfo, @@ -865,6 +871,13 @@ Instruction *DepInst = DepInfo.getInst(); if (DepInfo.isClobber()) { + + // Load immediately after lifetime end -> undef + if (isLifetimeEnd(DepInst)) { + Res = AvailableValue::get(UndefValue::get(LI->getType())); + return true; + } + // If the dependence is to a store that writes to a superset of the bits // read by the load, we can extract the bits we need for the load from the // stored value. diff --git a/llvm/test/Transforms/GVN/lifetime-end-store.ll b/llvm/test/Transforms/GVN/lifetime-end-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GVN/lifetime-end-store.ll @@ -0,0 +1,14 @@ +; RUN: opt < %s -basicaa -gvn -S | FileCheck %s + +define i8 @test(i8* %P) nounwind { +; CHECK: lifetime.end +; CHECK-NOT: load +; CHECK: ret i8 3 +entry: + call void @llvm.lifetime.end(i64 32, i8* %P) + store i8 3, i8* %P + %0 = load i8, i8* %P + ret i8 %0 +} + +declare void @llvm.lifetime.end(i64 %S, i8* nocapture %P) diff --git a/llvm/test/Transforms/GVN/lifetime-end.ll b/llvm/test/Transforms/GVN/lifetime-end.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GVN/lifetime-end.ll @@ -0,0 +1,13 @@ +; RUN: opt < %s -basicaa -gvn -S | FileCheck %s + +define i8 @test(i8* %P) nounwind { +; CHECK: lifetime.end +; CHECK-NOT: load +; CHECK: ret i8 undef +entry: + call void @llvm.lifetime.end(i64 32, i8* %P) + %0 = load i8, i8* %P + ret i8 %0 +} + +declare void @llvm.lifetime.end(i64 %S, i8* nocapture %P)