diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -807,6 +807,14 @@ return callCapturesBefore(I, MemoryLocation(P, Size), DT); } + /// Return information about whether a particular call site modifies + /// or reads the specified memory object \p Object. The caller must ensure the + /// object does not escape before the call. + ModRefInfo callNoCapturesBefore(const CallBase *Call, const Value *Object) { + AAQueryInfo AAQIP; + return callNoCapturesBefore(Call, Object, AAQIP); + } + /// @} //===--------------------------------------------------------------------===// /// \name Higher level methods for querying mod/ref information. @@ -871,6 +879,11 @@ ModRefInfo callCapturesBefore(const Instruction *I, const MemoryLocation &MemLoc, DominatorTree *DT, AAQueryInfo &AAQIP); + /// Return information about whether a particular call site modifies + /// or reads the specified memory object \p Object. The caller must ensure the + /// object does not escape before the call. + ModRefInfo callNoCapturesBefore(const CallBase *Call, const Value *Object, + AAQueryInfo &AAQIP); class Concept; diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -735,6 +735,18 @@ /* include Object */ true)) return ModRefInfo::ModRef; + return callNoCapturesBefore(Call, Object, AAQI); +} + +ModRefInfo AAResults::callNoCapturesBefore(const CallBase *Call, + const Value *Object, + AAQueryInfo &AAQI) { + assert(isIdentifiedFunctionLocal(Object) && + "Must be called with local objects only!"); + + if (Call == Object) + return ModRefInfo::ModRef; + unsigned ArgNo = 0; ModRefInfo R = ModRefInfo::NoModRef; bool IsMustAlias = true; diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -1326,12 +1326,22 @@ return false; } } + ModRefInfo MR = BatchAA.getModRefInfo(UseInst, DefLoc); + // If necessary, perform additional analysis. + if (isRefSet(MR)) { + if (auto *CB = dyn_cast(UseInst)) { + auto *DefUO = getUnderlyingObject(DefLoc.Ptr); + if (DefUO && notCapturedBeforeOrAt(DefUO, UseInst)) { + assert(!PointerMayBeCapturedBefore(DefLoc.Ptr, false, true, UseInst, + &DT, false, 0, &this->LI) && + "cached analysis disagrees with fresh " + "PointerMayBeCapturedBefore"); + MR = AA.callNoCapturesBefore(CB, DefUO); + } + } + } - // NOTE: For calls, the number of stores removed could be slightly improved - // by using AA.callCapturesBefore(UseInst, DefLoc, &DT), but that showed to - // be expensive compared to the benefits in practice. For now, avoid more - // expensive analysis to limit compile-time. - return isRefSet(BatchAA.getModRefInfo(UseInst, DefLoc)); + return isRefSet(MR); } /// Returns true if a dependency between \p Current and \p KillingDef is diff --git a/llvm/test/Transforms/DeadStoreElimination/captures-before-call.ll b/llvm/test/Transforms/DeadStoreElimination/captures-before-call.ll --- a/llvm/test/Transforms/DeadStoreElimination/captures-before-call.ll +++ b/llvm/test/Transforms/DeadStoreElimination/captures-before-call.ll @@ -11,7 +11,6 @@ ; CHECK-NEXT: [[V2:%.*]] = alloca i32, align 4 ; CHECK-NEXT: store i32 0, i32* [[V1]], align 4 ; CHECK-NEXT: call void @escape(i32* nonnull [[V1]]) -; CHECK-NEXT: store i32 55555, i32* [[V2]], align 4 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 [[CALL]], i32* [[V2]], align 4 ; CHECK-NEXT: call void @escape(i32* nonnull [[V2]]) @@ -46,7 +45,6 @@ define i32 @test_not_captured_before_call_same_bb() { ; CHECK-LABEL: @test_not_captured_before_call_same_bb( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: call void @escape_and_clobber(i32* [[A]]) @@ -63,7 +61,6 @@ define i32 @test_not_captured_before_call_same_bb_escape_unreachable_block() { ; CHECK-LABEL: @test_not_captured_before_call_same_bb_escape_unreachable_block( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: call void @escape_and_clobber(i32* [[A]]) @@ -106,7 +103,6 @@ define i32 @test_captured_after_call_same_bb_2_clobbered_later() { ; CHECK-LABEL: @test_captured_after_call_same_bb_2_clobbered_later( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: call void @escape_writeonly(i32* [[A]]) ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 @@ -125,7 +121,6 @@ define i32 @test_captured_sibling_path_to_call_other_blocks_1(i1 %c.1) { ; CHECK-LABEL: @test_captured_sibling_path_to_call_other_blocks_1( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: call void @escape_writeonly(i32* [[A]]) @@ -296,7 +291,6 @@ define i32 @test_not_captured_before_call_other_blocks_1(i1 %c.1) { ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_1( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]] @@ -328,7 +322,6 @@ define i32 @test_not_captured_before_call_other_blocks_2(i1 %c.1) { ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_2( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]] @@ -362,7 +355,6 @@ define i32 @test_not_captured_before_call_other_blocks_3(i1 %c.1) { ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_3( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]] @@ -394,7 +386,6 @@ define i32 @test_not_captured_before_call_other_blocks_4(i1 %c.1) { ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_4( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]] ; CHECK: then: ; CHECK-NEXT: br label [[EXIT:%.*]] @@ -431,7 +422,6 @@ ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_5( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[EXIT:%.*]] ; CHECK: then: ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() @@ -499,7 +489,6 @@ ; CHECK-LABEL: @test_not_captured_before_call_other_blocks_7( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() ; CHECK-NEXT: call void @escape_writeonly(i32* [[A]]) ; CHECK-NEXT: br i1 [[C_1:%.*]], label [[THEN:%.*]], label [[EXIT:%.*]] @@ -554,7 +543,6 @@ define i32 @test_captured_after_loop(i1 %c.1) { ; CHECK-LABEL: @test_captured_after_loop( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[R:%.*]] = call i32 @getval() @@ -613,7 +601,6 @@ ; CHECK-NEXT: bb: ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 ; CHECK-NEXT: [[EXT_A:%.*]] = bitcast i64* [[A]] to i8* -; CHECK-NEXT: store i64 0, i64* [[A]], align 8 ; CHECK-NEXT: call void @clobber() ; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[EXT_A]], i8* [[SRC:%.*]], i64 8, i1 false) ; CHECK-NEXT: store i64* [[A]], i64** [[ESCAPE:%.*]], align 8 @@ -641,7 +628,6 @@ ; CHECK-LABEL: @test_invoke_captures( ; CHECK-NEXT: bb: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: invoke void @clobber() ; CHECK-NEXT: to label [[BB2:%.*]] unwind label [[BB5:%.*]] ; CHECK: bb2: @@ -695,7 +681,6 @@ define i32 @test_not_captured_before_load_same_bb_noalias_call() { ; CHECK-LABEL: @test_not_captured_before_load_same_bb_noalias_call( ; CHECK-NEXT: [[A:%.*]] = call i32* @alloc() -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval_nounwind() ; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: call void @escape_and_clobber(i32* [[A]]) @@ -711,9 +696,8 @@ define i32 @test_not_captured_before_load_same_bb_noalias_arg(i32* noalias %a) { ; CHECK-LABEL: @test_not_captured_before_load_same_bb_noalias_arg( -; CHECK-NEXT: store i32 55, i32* [[A:%.*]], align 4 ; CHECK-NEXT: [[R:%.*]] = call i32 @getval_nounwind() -; CHECK-NEXT: store i32 99, i32* [[A]], align 4 +; CHECK-NEXT: store i32 99, i32* [[A:%.*]], align 4 ; CHECK-NEXT: call void @escape_and_clobber(i32* [[A]]) ; CHECK-NEXT: ret i32 [[R]] ; diff --git a/llvm/test/Transforms/DeadStoreElimination/captures-before-load.ll b/llvm/test/Transforms/DeadStoreElimination/captures-before-load.ll --- a/llvm/test/Transforms/DeadStoreElimination/captures-before-load.ll +++ b/llvm/test/Transforms/DeadStoreElimination/captures-before-load.ll @@ -155,7 +155,6 @@ define i32 @test_not_captured_before_load_same_bb_clobber(i32** %in.ptr) { ; CHECK-LABEL: @test_not_captured_before_load_same_bb_clobber( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 55, i32* [[A]], align 4 ; CHECK-NEXT: call void @clobber() ; CHECK-NEXT: [[IN_LV_1:%.*]] = load i32*, i32** [[IN_PTR:%.*]], align 2 ; CHECK-NEXT: [[IN_LV_2:%.*]] = load i32, i32* [[IN_LV_1]], align 2 @@ -243,7 +242,6 @@ ; CHECK-NEXT: [[IN_LV_2:%.*]] = load i32, i32* [[IN_LV_1]], align 2 ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: exit: -; CHECK-NEXT: store i32 99, i32* [[A]], align 4 ; CHECK-NEXT: call void @clobber() ; CHECK-NEXT: ret i32 [[IN_LV_2]] ;