Index: lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- lib/Transforms/Scalar/EarlyCSE.cpp +++ lib/Transforms/Scalar/EarlyCSE.cpp @@ -658,6 +658,17 @@ if (match(Inst, m_Intrinsic())) { if (auto *CondI = dyn_cast(cast(Inst)->getArgOperand(0))) { + // Do we already know the actual value of this condition? + if (auto *KnownCond = AvailableValues.lookup(CondI)) { + // Is the condition known to be true? + if (KnownCond == ConstantInt::getTrue(BB->getContext())) { + DEBUG(dbgs() << "EarlyCSE removing guard: " << *Inst << '\n'); + removeMSSA(Inst); + Inst->eraseFromParent(); + Changed = true; + continue; + } + } // The condition we're on guarding here is true for all dominated // locations. if (SimpleValue::canHandle(CondI)) Index: test/Transforms/EarlyCSE/guards.ll =================================================================== --- test/Transforms/EarlyCSE/guards.ll +++ test/Transforms/EarlyCSE/guards.ll @@ -3,11 +3,11 @@ declare void @llvm.experimental.guard(i1,...) -define i32 @test0(i32* %ptr, i1 %cond) { +define i32 @test_00(i32* %ptr, i1 %cond) { ; We can do store to load forwarding over a guard, since it does not ; clobber memory -; CHECK-LABEL: @test0( +; CHECK-LABEL: @test_00( ; CHECK-NEXT: store i32 40, i32* %ptr ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] ; CHECK-NEXT: ret i32 40 @@ -18,10 +18,10 @@ ret i32 %rval } -define i32 @test1(i32* %val, i1 %cond) { +define i32 @test_01(i32* %val, i1 %cond) { ; We can CSE loads over a guard, since it does not clobber memory -; CHECK-LABEL: @test1( +; CHECK-LABEL: @test_01( ; CHECK-NEXT: %val0 = load i32, i32* %val ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] ; CHECK-NEXT: ret i32 0 @@ -33,20 +33,20 @@ ret i32 %rval } -define i32 @test2() { +define i32 @test_02() { ; Guards on "true" get removed -; CHECK-LABEL: @test2( +; CHECK-LABEL: @test_02( ; CHECK-NEXT: ret i32 0 call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ] ret i32 0 } -define i32 @test3(i32 %val) { +define i32 @test_03(i32 %val) { ; After a guard has executed the condition it was guarding is known to ; be true. -; CHECK-LABEL: @test3( +; CHECK-LABEL: @test_03( ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] ; CHECK-NEXT: ret i32 -1 @@ -61,11 +61,11 @@ ret i32 %rval } -define i32 @test3.unhandled(i32 %val) { +define i32 @test_03.unhandled(i32 %val) { ; After a guard has executed the condition it was guarding is known to ; be true. -; CHECK-LABEL: @test3.unhandled( +; CHECK-LABEL: @test_03.unhandled( ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] ; CHECK-NEXT: %cond1 = icmp sge i32 %val, 40 @@ -81,10 +81,10 @@ ret i32 0 } -define i32 @test4(i32 %val, i1 %c) { +define i32 @test_04(i32 %val, i1 %c) { ; Same as test3, but with some control flow involved. -; CHECK-LABEL: @test4( +; CHECK-LABEL: @test_04( ; CHECK: entry: ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0 @@ -122,10 +122,10 @@ ret i32 20 } -define i32 @test5(i32 %val, i1 %c) { +define i32 @test_05(i32 %val, i1 %c) { ; Same as test4, but the %left block has mutliple predecessors. -; CHECK-LABEL: @test5( +; CHECK-LABEL: @test_05( ; CHECK: entry: ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 @@ -164,12 +164,12 @@ br label %left } -define void @test6(i1 %c, i32* %ptr) { +define void @test_06(i1 %c, i32* %ptr) { ; Check that we do not DSE over calls to @llvm.experimental.guard. ; Guard intrinsics do _read_ memory, so th call to guard below needs ; to see the store of 500 to %ptr -; CHECK-LABEL: @test6( +; CHECK-LABEL: @test_06( ; CHECK-NEXT: store i32 500, i32* %ptr ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"() ] ; CHECK-NEXT: store i32 600, i32* %ptr @@ -180,3 +180,174 @@ store i32 600, i32* %ptr ret void } + +define void @test_07(i32 %a, i32 %b) { +; Check that we are able to remove the guards on the same condition even if the +; condition is not being recalculated. + +; CHECK-LABEL: @test_07( +; CHECK-NEXT: %cmp = icmp eq i32 %a, %b +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] +; CHECK-NEXT: ret void + + %cmp = icmp eq i32 %a, %b + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + ret void +} + +define void @test_08(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: @test_08( +; 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 + + %cmp = icmp eq i32 %a, %b + store i32 100, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 200, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 300, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 400, i32* %ptr + ret void +} + +define void @test_09(i32 %a, i32 %b, i1 %c, i32* %ptr) { +; Similar to test_08, 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: @test_09( +; CHECK: 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 + +entry: + %cmp = icmp eq i32 %a, %b + store i32 100, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 200, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 300, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 400, i32* %ptr + br i1 %c, label %if.true, label %if.false + +if.true: + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 500, i32* %ptr + br label %merge + +if.false: + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 600, i32* %ptr + br label %merge + +merge: + ret void +} + +define void @test_09.unhandled(i32 %a, i32 %b, i32* %ptr) { +; Demonstrates the case we do not currently cover: branch by guarded condition. +; We can replace the branching by cmp condition by unconditional branch. + +; CHECK-LABEL: @test_09.unhandled( +; CHECK: 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 %cmp, 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: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: store i32 600, i32* %ptr +; CHECK-NEXT: br label %merge +; CHECK: merge: +; CHECK-NEXT: ret void + +entry: + %cmp = icmp eq i32 %a, %b + store i32 100, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 200, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 300, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 400, i32* %ptr + br i1 %cmp, label %if.true, label %if.false + +if.true: + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 500, i32* %ptr + br label %merge + +if.false: + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 600, i32* %ptr + br label %merge + +merge: + ret void +} + +define void @test_10(i32 %a, i32 %b, i1 %c, i32* %ptr) { +; Make sure that non-dominating guards do not cause other guards removal. + +; CHECK-LABEL: @test_10( +; CHECK: entry: +; CHECK-NEXT: %cmp = icmp eq i32 %a, %b +; CHECK-NEXT: br i1 %c, label %if.true, label %if.false +; CHECK: if.true: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] +; CHECK-NEXT: store i32 100, i32* %ptr +; CHECK-NEXT: br label %merge +; CHECK: if.false: +; CHECK-NEXT: store i32 200, i32* %ptr +; CHECK-NEXT: br label %merge +; CHECK: merge: +; CHECK-NEXT: store i32 300, i32* %ptr +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] +; CHECK-NEXT: store i32 400, i32* %ptr +; CHECK-NEXT: ret void + +entry: + %cmp = icmp eq i32 %a, %b + br i1 %c, label %if.true, label %if.false + +if.true: + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 100, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + br label %merge + +if.false: + store i32 200, i32* %ptr + br label %merge + +merge: + store i32 300, i32* %ptr + call void (i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ] + store i32 400, i32* %ptr + ret void +}