diff --git a/llvm/include/llvm/IR/KnowledgeRetention.h b/llvm/include/llvm/IR/KnowledgeRetention.h --- a/llvm/include/llvm/IR/KnowledgeRetention.h +++ b/llvm/include/llvm/IR/KnowledgeRetention.h @@ -31,6 +31,11 @@ /// The returned instruction is not inserted anywhere. IntrinsicInst *buildAssumeFromInst(Instruction *I); +/// Calls BuildAssumeFromInst and if the resulting llvm.assume is valid insert +/// if before I. This is usually what need to be done to salvage the knowledge +/// contained in the instruction I. +void salvageKnowledge(Instruction* I); + /// It is possible to have multiple Value for the argument of an attribute in /// the same llvm.assume on the same llvm::Value. This is rare but need to be /// dealt with. diff --git a/llvm/lib/IR/KnowledgeRetention.cpp b/llvm/lib/IR/KnowledgeRetention.cpp --- a/llvm/lib/IR/KnowledgeRetention.cpp +++ b/llvm/lib/IR/KnowledgeRetention.cpp @@ -232,6 +232,11 @@ return Builder.build(); } +void llvm::salvageKnowledge(Instruction* I) { + if (Instruction* Intr = buildAssumeFromInst(I)) + Intr->insertBefore(I); +} + static bool bundleHasArgument(const CallBase::BundleOpInfo &BOI, unsigned Idx) { return BOI.End - BOI.Begin > Idx; diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -38,6 +38,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/KnowledgeRetention.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" @@ -947,6 +948,7 @@ continue; } + salvageKnowledge(&Inst); salvageDebugInfoOrMarkUndef(Inst); removeMSSA(Inst); Inst.eraseFromParent(); @@ -1013,6 +1015,7 @@ cast(KnownCond)->isOne()) { LLVM_DEBUG(dbgs() << "EarlyCSE removing guard: " << Inst << '\n'); + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1048,6 +1051,7 @@ Changed = true; } if (isInstructionTriviallyDead(&Inst, &TLI)) { + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1073,6 +1077,7 @@ if (auto *I = dyn_cast(V)) I->andIRFlags(&Inst); Inst.replaceAllUsesWith(V); + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1133,6 +1138,7 @@ } if (!Inst.use_empty()) Inst.replaceAllUsesWith(Op); + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1176,6 +1182,7 @@ } if (!Inst.use_empty()) Inst.replaceAllUsesWith(InVal.first); + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1228,6 +1235,7 @@ LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); continue; } + salvageKnowledge(&Inst); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1263,6 +1271,7 @@ if (!DebugCounter::shouldExecute(CSECounter)) { LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); } else { + salvageKnowledge(&Inst); removeMSSA(*LastStore); LastStore->eraseFromParent(); Changed = true; diff --git a/llvm/test/Transforms/EarlyCSE/guards.ll b/llvm/test/Transforms/EarlyCSE/guards.ll --- a/llvm/test/Transforms/EarlyCSE/guards.ll +++ b/llvm/test/Transforms/EarlyCSE/guards.ll @@ -10,10 +10,16 @@ define i32 @test0(i32* %ptr, i1 %cond) { ; We can do store to load forwarding over a guard, since it does not ; clobber memory -; CHECK-LABEL: @test0( -; CHECK-NEXT: store i32 40, i32* [[PTR:%.*]] -; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] -; CHECK-NEXT: ret i32 40 +; NO_ASSUME-LABEL: @test0( +; NO_ASSUME-NEXT: store i32 40, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] +; NO_ASSUME-NEXT: ret i32 40 +; +; USE_ASSUME-LABEL: @test0( +; USE_ASSUME-NEXT: store i32 40, i32* [[PTR:%.*]] +; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: ret i32 40 ; store i32 40, i32* %ptr @@ -24,10 +30,16 @@ define i32 @test1(i32* %val, i1 %cond) { ; We can CSE loads over a guard, since it does not clobber memory -; CHECK-LABEL: @test1( -; CHECK-NEXT: [[VAL0:%.*]] = load i32, i32* [[VAL:%.*]] -; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: @test1( +; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[VAL:%.*]] +; NO_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: @test1( +; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[VAL:%.*]] +; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"() ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[VAL]], i64 4), "nonnull"(i32* [[VAL]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; %val0 = load i32, i32* %val @@ -205,12 +217,21 @@ define void @test08(i32 %a, i32 %b, i32* %ptr) { ; Check that we deal correctly with stores when removing guards in the same ; block in case when the condition is not recalculated. -; CHECK-LABEL: @test08( -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: store i32 100, i32* [[PTR:%.*]] -; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] -; CHECK-NEXT: store i32 400, i32* [[PTR]] -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test08( +; NO_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; NO_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] +; NO_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test08( +; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; USE_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] +; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; USE_ASSUME-NEXT: ret void ; %cmp = icmp eq i32 %a, %b @@ -228,21 +249,39 @@ ; Similar to test08, but with more control flow. ; TODO: Can we get rid of the store in the end of entry given that it is ; post-dominated by other stores? -; CHECK-LABEL: @test09( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: store i32 100, i32* [[PTR:%.*]] -; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] -; CHECK-NEXT: store i32 400, i32* [[PTR]] -; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] -; CHECK: if.true: -; CHECK-NEXT: store i32 500, i32* [[PTR]] -; CHECK-NEXT: br label [[MERGE:%.*]] -; CHECK: if.false: -; CHECK-NEXT: store i32 600, i32* [[PTR]] -; CHECK-NEXT: br label [[MERGE]] -; CHECK: merge: -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test09( +; NO_ASSUME-NEXT: entry: +; NO_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; NO_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] +; NO_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; NO_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; NO_ASSUME: if.true: +; NO_ASSUME-NEXT: store i32 500, i32* [[PTR]] +; NO_ASSUME-NEXT: br label [[MERGE:%.*]] +; NO_ASSUME: if.false: +; NO_ASSUME-NEXT: store i32 600, i32* [[PTR]] +; NO_ASSUME-NEXT: br label [[MERGE]] +; NO_ASSUME: merge: +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test09( +; USE_ASSUME-NEXT: entry: +; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; USE_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] +; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; USE_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; USE_ASSUME: if.true: +; USE_ASSUME-NEXT: store i32 500, i32* [[PTR]] +; USE_ASSUME-NEXT: br label [[MERGE:%.*]] +; USE_ASSUME: if.false: +; USE_ASSUME-NEXT: store i32 600, i32* [[PTR]] +; USE_ASSUME-NEXT: br label [[MERGE]] +; USE_ASSUME: merge: +; USE_ASSUME-NEXT: ret void ; entry: @@ -361,11 +400,20 @@ define void @test13(i32 %a, i32 %b, i32* %ptr) { ; Check that we deal correctly with stores when removing guards due to assume. -; CHECK-LABEL: @test13( -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) -; CHECK-NEXT: store i32 400, i32* [[PTR:%.*]] -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test13( +; NO_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; NO_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) +; NO_ASSUME-NEXT: store i32 400, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test13( +; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR:%.*]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; USE_ASSUME-NEXT: ret void ; %cmp = icmp eq i32 %a, %b @@ -384,20 +432,38 @@ ; Similar to test13, but with more control flow. ; TODO: Can we get rid of the store in the end of entry given that it is ; post-dominated by other stores? -; CHECK-LABEL: @test14( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) -; CHECK-NEXT: store i32 400, i32* [[PTR:%.*]] -; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] -; CHECK: if.true: -; CHECK-NEXT: store i32 500, i32* [[PTR]] -; CHECK-NEXT: br label [[MERGE:%.*]] -; CHECK: if.false: -; CHECK-NEXT: store i32 600, i32* [[PTR]] -; CHECK-NEXT: br label [[MERGE]] -; CHECK: merge: -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test14( +; NO_ASSUME-NEXT: entry: +; NO_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; NO_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) +; NO_ASSUME-NEXT: store i32 400, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; NO_ASSUME: if.true: +; NO_ASSUME-NEXT: store i32 500, i32* [[PTR]] +; NO_ASSUME-NEXT: br label [[MERGE:%.*]] +; NO_ASSUME: if.false: +; NO_ASSUME-NEXT: store i32 600, i32* [[PTR]] +; NO_ASSUME-NEXT: br label [[MERGE]] +; NO_ASSUME: merge: +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test14( +; USE_ASSUME-NEXT: entry: +; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR:%.*]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] +; USE_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; USE_ASSUME: if.true: +; USE_ASSUME-NEXT: store i32 500, i32* [[PTR]] +; USE_ASSUME-NEXT: br label [[MERGE:%.*]] +; USE_ASSUME: if.false: +; USE_ASSUME-NEXT: store i32 600, i32* [[PTR]] +; USE_ASSUME-NEXT: br label [[MERGE]] +; USE_ASSUME: merge: +; USE_ASSUME-NEXT: ret void ; entry: diff --git a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll --- a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll +++ b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll @@ -6,12 +6,21 @@ declare void @clobber_and_use(i32) define void @f_0(i32* %ptr) { -; CHECK-LABEL: @f_0( -; CHECK-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @f_0( +; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @f_0( +; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: ret void ; %val0 = load i32, i32* %ptr, !invariant.load !{} @@ -25,11 +34,18 @@ define void @f_1(i32* %ptr) { ; We can forward invariant loads to non-invariant loads. -; CHECK-LABEL: @f_1( -; CHECK-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @f_1( +; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @f_1( +; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: ret void ; %val0 = load i32, i32* %ptr, !invariant.load !{} @@ -41,11 +57,18 @@ define void @f_2(i32* %ptr) { ; We can forward a non-invariant load into an invariant load. -; CHECK-LABEL: @f_2( -; CHECK-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]] -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @f_2( +; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]] +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @f_2( +; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: ret void ; %val0 = load i32, i32* %ptr @@ -56,15 +79,26 @@ } define void @f_3(i1 %cond, i32* %ptr) { -; CHECK-LABEL: @f_3( -; CHECK-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]] -; CHECK: left: -; CHECK-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; CHECK-NEXT: ret void -; CHECK: right: -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @f_3( +; NO_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]] +; NO_ASSUME: left: +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; NO_ASSUME-NEXT: ret void +; NO_ASSUME: right: +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @f_3( +; USE_ASSUME-NEXT: [[VAL0:%.*]] = load i32, i32* [[PTR:%.*]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]] +; USE_ASSUME: left: +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) +; USE_ASSUME-NEXT: ret void +; USE_ASSUME: right: +; USE_ASSUME-NEXT: ret void ; %val0 = load i32, i32* %ptr, !invariant.load !{} call void @clobber_and_use(i32 %val0) @@ -114,10 +148,16 @@ ; for the moment we chose to be conservative and just assume it's valid ; to restore the same unchanging value. define void @test_dse1(i32* %p) { -; CHECK-LABEL: @test_dse1( -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 -; CHECK-NEXT: call void @clobber_and_use(i32 [[V1]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test_dse1( +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test_dse1( +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret void ; %v1 = load i32, i32* %p, !invariant.load !{} call void @clobber_and_use(i32 %v1) @@ -142,12 +182,21 @@ ; If we remove the load, we still start an invariant scope since ; it lets us remove later loads not explicitly marked invariant define void @test_scope_start_without_load(i32* %p) { -; CHECK-LABEL: @test_scope_start_without_load( -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]] -; CHECK-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] -; CHECK-NEXT: call void @clobber_and_use(i32 [[ADD]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[V1]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test_scope_start_without_load( +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]] +; NO_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test_scope_start_without_load( +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]] +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; USE_ASSUME-NEXT: ret void ; %v1 = load i32, i32* %p %v2 = load i32, i32* %p, !invariant.load !{} @@ -162,13 +211,23 @@ ; with a potentially greater generation. This hides the earlier invariant ; load define void @test_scope_restart(i32* %p) { -; CHECK-LABEL: @test_scope_restart( -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 -; CHECK-NEXT: call void @clobber_and_use(i32 [[V1]]) -; CHECK-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] -; CHECK-NEXT: call void @clobber_and_use(i32 [[ADD]]) -; CHECK-NEXT: call void @clobber_and_use(i32 [[V1]]) -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: @test_scope_restart( +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; NO_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) +; NO_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: @test_scope_restart( +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P:%.*]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) +; USE_ASSUME-NEXT: ret void ; %v1 = load i32, i32* %p, !invariant.load !{} call void @clobber_and_use(i32 %v1) diff --git a/llvm/test/Transforms/EarlyCSE/invariant.start.ll b/llvm/test/Transforms/EarlyCSE/invariant.start.ll --- a/llvm/test/Transforms/EarlyCSE/invariant.start.ll +++ b/llvm/test/Transforms/EarlyCSE/invariant.start.ll @@ -9,11 +9,18 @@ ; Check that we do load-load forwarding over invariant.start, since it does not ; clobber memory define i8 @test_bypass1(i8 *%P) { -; CHECK-LABEL: define {{[^@]+}}@test_bypass1 -; CHECK-SAME: (i8* [[P:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i8, i8* [[P]] -; CHECK-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) -; CHECK-NEXT: ret i8 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass1 +; NO_ASSUME-SAME: (i8* [[P:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i8, i8* [[P]] +; NO_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; NO_ASSUME-NEXT: ret i8 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass1 +; USE_ASSUME-SAME: (i8* [[P:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i8, i8* [[P]] +; USE_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[P]], i64 1), "nonnull"(i8* [[P]]) ] +; USE_ASSUME-NEXT: ret i8 0 ; %V1 = load i8, i8* %P @@ -26,11 +33,18 @@ ; Trivial Store->load forwarding over invariant.start define i8 @test_bypass2(i8 *%P) { -; CHECK-LABEL: define {{[^@]+}}@test_bypass2 -; CHECK-SAME: (i8* [[P:%.*]]) -; CHECK-NEXT: store i8 42, i8* [[P]] -; CHECK-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) -; CHECK-NEXT: ret i8 42 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass2 +; NO_ASSUME-SAME: (i8* [[P:%.*]]) +; NO_ASSUME-NEXT: store i8 42, i8* [[P]] +; NO_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; NO_ASSUME-NEXT: ret i8 42 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass2 +; USE_ASSUME-SAME: (i8* [[P:%.*]]) +; USE_ASSUME-NEXT: store i8 42, i8* [[P]] +; USE_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[P]], i64 1), "nonnull"(i8* [[P]]) ] +; USE_ASSUME-NEXT: ret i8 42 ; store i8 42, i8* %P @@ -43,11 +57,18 @@ ; %P is valid, and the second store is actually unreachable based on semantics ; of invariant.start. define void @test_bypass3(i8* %P) { -; CHECK-LABEL: define {{[^@]+}}@test_bypass3 -; CHECK-SAME: (i8* [[P:%.*]]) -; CHECK-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) -; CHECK-NEXT: store i8 60, i8* [[P]] -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: define {{[^@]+}}@test_bypass3 +; NO_ASSUME-SAME: (i8* [[P:%.*]]) +; NO_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; NO_ASSUME-NEXT: store i8 60, i8* [[P]] +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_bypass3 +; USE_ASSUME-SAME: (i8* [[P:%.*]]) +; USE_ASSUME-NEXT: [[I:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 1, i8* [[P]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[P]], i64 1), "nonnull"(i8* [[P]]) ] +; USE_ASSUME-NEXT: store i8 60, i8* [[P]] +; USE_ASSUME-NEXT: ret void ; store i8 50, i8* %P @@ -83,12 +104,20 @@ declare void @llvm.invariant.end.p0i32({}*, i64, i32* nocapture) nounwind define i32 @test_before_load(i32* %p) { -; CHECK-LABEL: define {{[^@]+}}@test_before_load -; CHECK-SAME: (i32* [[P:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_before_load +; NO_ASSUME-SAME: (i32* [[P:%.*]]) +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_before_load +; USE_ASSUME-SAME: (i32* [[P:%.*]]) +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) %v1 = load i32, i32* %p @@ -99,12 +128,20 @@ } define i32 @test_before_clobber(i32* %p) { -; CHECK-LABEL: define {{[^@]+}}@test_before_clobber -; CHECK-SAME: (i32* [[P:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_before_clobber +; NO_ASSUME-SAME: (i32* [[P:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_before_clobber +; USE_ASSUME-SAME: (i32* [[P:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; %v1 = load i32, i32* %p call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) @@ -115,13 +152,22 @@ } define i32 @test_duplicate_scope(i32* %p) { -; CHECK-LABEL: define {{[^@]+}}@test_duplicate_scope -; CHECK-SAME: (i32* [[P:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: [[TMP2:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_duplicate_scope +; NO_ASSUME-SAME: (i32* [[P:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: [[TMP2:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_duplicate_scope +; USE_ASSUME-SAME: (i32* [[P:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: [[TMP2:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; %v1 = load i32, i32* %p call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) @@ -133,13 +179,22 @@ } define i32 @test_unanalzyable_load(i32* %p) { -; CHECK-LABEL: define {{[^@]+}}@test_unanalzyable_load -; CHECK-SAME: (i32* [[P:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_unanalzyable_load +; NO_ASSUME-SAME: (i32* [[P:%.*]]) +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_unanalzyable_load +; USE_ASSUME-SAME: (i32* [[P:%.*]]) +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) call void @clobber() @@ -169,16 +224,28 @@ } define i32 @test_merge(i32* %p, i1 %cnd) { -; CHECK-LABEL: define {{[^@]+}}@test_merge -; CHECK-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] -; CHECK: taken: -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: br label [[MERGE]] -; CHECK: merge: -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_merge +; NO_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; NO_ASSUME: taken: +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: br label [[MERGE]] +; NO_ASSUME: merge: +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_merge +; USE_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; USE_ASSUME: taken: +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: br label [[MERGE]] +; USE_ASSUME: merge: +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; %v1 = load i32, i32* %p call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) @@ -250,17 +317,30 @@ } define i32 @test_merge_unanalyzable_load(i32* %p, i1 %cnd) { -; CHECK-LABEL: define {{[^@]+}}@test_merge_unanalyzable_load -; CHECK-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] -; CHECK: taken: -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: br label [[MERGE]] -; CHECK: merge: -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_merge_unanalyzable_load +; NO_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; NO_ASSUME: taken: +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: br label [[MERGE]] +; NO_ASSUME: merge: +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_merge_unanalyzable_load +; USE_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: br i1 [[CND]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; USE_ASSUME: taken: +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: br label [[MERGE]] +; USE_ASSUME: merge: +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) call void @clobber() @@ -277,12 +357,20 @@ } define void @test_dse_before_load(i32* %p, i1 %cnd) { -; CHECK-LABEL: define {{[^@]+}}@test_dse_before_load -; CHECK-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: define {{[^@]+}}@test_dse_before_load +; NO_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_dse_before_load +; USE_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret void ; call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) %v1 = load i32, i32* %p @@ -292,12 +380,20 @@ } define void @test_dse_after_load(i32* %p, i1 %cnd) { -; CHECK-LABEL: define {{[^@]+}}@test_dse_after_load -; CHECK-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]] -; CHECK-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret void +; NO_ASSUME-LABEL: define {{[^@]+}}@test_dse_after_load +; NO_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; NO_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret void +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_dse_after_load +; USE_ASSUME-SAME: (i32* [[P:%.*]], i1 [[CND:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]] +; USE_ASSUME-NEXT: [[TMP1:%.*]] = call {}* @llvm.invariant.start.p0i32(i64 4, i32* [[P]]) +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret void ; %v1 = load i32, i32* %p call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) @@ -410,11 +506,18 @@ ; Invariant load defact starts an invariant.start scope of the appropriate size define i32 @test_invariant_load_scope(i32* %p) { -; CHECK-LABEL: define {{[^@]+}}@test_invariant_load_scope -; CHECK-SAME: (i32* [[P:%.*]]) -; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[P]], !invariant.load !0 -; CHECK-NEXT: call void @clobber() -; CHECK-NEXT: ret i32 0 +; NO_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope +; NO_ASSUME-SAME: (i32* [[P:%.*]]) +; NO_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]], !invariant.load !0 +; NO_ASSUME-NEXT: call void @clobber() +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: define {{[^@]+}}@test_invariant_load_scope +; USE_ASSUME-SAME: (i32* [[P:%.*]]) +; USE_ASSUME-NEXT: [[V1:%.*]] = load i32, i32* [[P]], !invariant.load !0 +; USE_ASSUME-NEXT: call void @clobber() +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; USE_ASSUME-NEXT: ret i32 0 ; %v1 = load i32, i32* %p, !invariant.load !{} call void @clobber()