diff --git a/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/lib/Transforms/Scalar/MemCpyOptimizer.cpp index 8976742be40..3c421d2365d 100644 --- a/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -496,7 +496,7 @@ static unsigned findCommonAlignment(const DataLayout &DL, const StoreInst *SI, // This method try to lift a store instruction before position P. // It will lift the store and its argument + that anything that -// lay alias with these. +// may alias with these. // The method returns true if it was successful. static bool moveUp(AliasAnalysis &AA, StoreInst *SI, Instruction *P) { // If the store alias this position, early bail out. @@ -675,6 +675,8 @@ bool MemCpyOpt::processStore(StoreInst *SI, BasicBlock::iterator &BBI) { if (C) { // Check that nothing touches the dest of the "copy" between // the call and the store. + Value *CpyDest = SI->getPointerOperand()->stripPointerCasts(); + bool CpyDestIsLocal = isa(CpyDest); AliasAnalysis &AA = getAnalysis().getAAResults(); MemoryLocation StoreLoc = MemoryLocation::get(SI); for (BasicBlock::iterator I = --SI->getIterator(), E = C->getIterator(); @@ -683,6 +685,12 @@ bool MemCpyOpt::processStore(StoreInst *SI, BasicBlock::iterator &BBI) { C = nullptr; break; } + // The store to dest may never happen if an exception can be thrown + // between the load and the store. + if (I->mayThrow() && !CpyDestIsLocal) { + C = nullptr; + break; + } } } @@ -815,6 +823,10 @@ bool MemCpyOpt::performCallSlotOptzn(Instruction *cpy, if (destSize < srcSize) return false; } else if (Argument *A = dyn_cast(cpyDest)) { + // The store to dest may never happen if the call can throw. + if (C->mayThrow()) + return false; + if (A->getDereferenceableBytes() < srcSize) { // If the destination is an sret parameter then only accesses that are // outside of the returned struct type can trap. diff --git a/test/Transforms/MemCpyOpt/callslot_throw.ll b/test/Transforms/MemCpyOpt/callslot_throw.ll new file mode 100644 index 00000000000..1aa4c92efc7 --- /dev/null +++ b/test/Transforms/MemCpyOpt/callslot_throw.ll @@ -0,0 +1,34 @@ +; RUN: opt -S -memcpyopt < %s | FileCheck %s +declare void @may_throw(i32* nocapture %x) + +; CHECK-LABEL: define void @test1( +define void @test1(i32* nocapture noalias dereferenceable(4) %x) { +entry: + %t = alloca i32, align 4 + call void @may_throw(i32* nonnull %t) + %load = load i32, i32* %t, align 4 + store i32 %load, i32* %x, align 4 +; CHECK: %[[t:.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @may_throw(i32* {{.*}} %[[t]]) +; CHECK-NEXT: %[[load:.*]] = load i32, i32* %[[t]], align 4 +; CHECK-NEXT: store i32 %[[load]], i32* %x, align 4 + ret void +} + +declare void @always_throws() + +; CHECK-LABEL: define void @test2( +define void @test2(i32* nocapture noalias dereferenceable(4) %x) { +entry: + %t = alloca i32, align 4 + call void @may_throw(i32* nonnull %t) nounwind + %load = load i32, i32* %t, align 4 + call void @always_throws() + store i32 %load, i32* %x, align 4 +; CHECK: %[[t:.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @may_throw(i32* {{.*}} %[[t]]) +; CHECK-NEXT: %[[load:.*]] = load i32, i32* %[[t]], align 4 +; CHECK-NEXT: call void @always_throws() +; CHECK-NEXT: store i32 %[[load]], i32* %x, align 4 + ret void +} diff --git a/test/Transforms/MemCpyOpt/loadstore-sret.ll b/test/Transforms/MemCpyOpt/loadstore-sret.ll index 55cbe59651f..4c6136cf625 100644 --- a/test/Transforms/MemCpyOpt/loadstore-sret.ll +++ b/test/Transforms/MemCpyOpt/loadstore-sret.ll @@ -22,4 +22,4 @@ _ZNSt8auto_ptrIiED1Ev.exit: ret void } -declare void @_Z3barv(%"class.std::auto_ptr"* nocapture sret) +declare void @_Z3barv(%"class.std::auto_ptr"* nocapture sret) nounwind