Index: lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- lib/Transforms/Scalar/EarlyCSE.cpp +++ lib/Transforms/Scalar/EarlyCSE.cpp @@ -635,9 +635,14 @@ // Skip assume intrinsics, they don't really have side effects (although // they're marked as such to ensure preservation of control dependencies), - // and this pass will not disturb any of the assumption's control - // dependencies. + // and this pass will not bother with its removal. However, we should mark + // its condition as true for all dominated blocks. if (match(Inst, m_Intrinsic())) { + if (auto *CondI = + dyn_cast(cast(Inst)->getArgOperand(0))) { + if (SimpleValue::canHandle(CondI)) + AvailableValues.insert(CondI, ConstantInt::getTrue(BB->getContext())); + } DEBUG(dbgs() << "EarlyCSE skipping assumption: " << *Inst << '\n'); continue; } Index: test/Transforms/EarlyCSE/guards.ll =================================================================== --- test/Transforms/EarlyCSE/guards.ll +++ test/Transforms/EarlyCSE/guards.ll @@ -3,6 +3,8 @@ declare void @llvm.experimental.guard(i1,...) +declare void @llvm.assume(i1) + define i32 @test_00(i32* %ptr, i1 %cond) { ; We can do store to load forwarding over a guard, since it does not ; clobber memory @@ -351,3 +353,128 @@ store i32 400, i32* %ptr ret void } + +define void @test_11(i32 %a, i32 %b) { +; Check that the assume marks its condition as being true (and thus allows to +; eliminate the dominated guards). + +; CHECK-LABEL: @test_11( +; CHECK-NEXT: %cmp = icmp eq i32 %a, %b +; CHECK-NEXT: call void @llvm.assume(i1 %cmp) +; CHECK-NEXT: ret void + + %cmp = icmp eq i32 %a, %b + call void @llvm.assume(i1 %cmp) + 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_12(i32 %a, i32 %b, i32* %ptr) { +; Check that we deal correctly with stores when removing guards due to assume. + +; CHECK-LABEL: @test_12( +; 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 + + %cmp = icmp eq i32 %a, %b + call void @llvm.assume(i1 %cmp) + 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_13(i32 %a, i32 %b, i1 %c, i32* %ptr) { +; Similar to test_12, 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_13( +; CHECK: 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 + +entry: + %cmp = icmp eq i32 %a, %b + call void @llvm.assume(i1 %cmp) + 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_14(i32 %a, i32 %b, i1 %c, i32* %ptr) { +; Make sure that non-dominating assumes do not cause guards removal. + +; CHECK-LABEL: @test_14( +; 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 @llvm.assume(i1 %cmp) +; 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 @llvm.assume(i1 %cmp) + 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 +}