Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1973,6 +1973,48 @@ } } + // Canonicalize widenable.conditions for ease of pattern matching + { + // Canonicalize WC to left when compexity is equal (otherwise, it's already there) + if (match(Op1, m_Intrinsic()) && + !match(Op0, m_Intrinsic())) { + I.swapOperands(); + return &I; + } + + // If we have more than one WC involved in an expression, kill one if + // either would leave the condition trivially widenable. + if (match(Op0, m_Intrinsic()) && + match(Op1, m_Intrinsic()) && + Op0->hasOneUse() && Op1->hasOneUse()) + return replaceInstUsesWith(I, Op0); + + Value *X, *Y; + // (WC & B) & A --> (A & B) & WC + // (B & WC) & A is canonicalized to previous + // A can't be WC due to canonicalize to LHS + if (match(Op0, m_OneUse(m_And(m_Value(X), m_Value(Y)))) && + match(X, m_Intrinsic())) { + cast(Op0)->setOperand(0, Op1); + I.setOperand(1, X); + return &I; + } + // A & (WC & C) --> WC & (A & C) + // A & (B & WC) is canonicalized to previous + // A might be WC + if (match(Op1, m_OneUse(m_And(m_Value(X), m_Value(Y)))) && + match(X, m_Intrinsic())) { + if (!match(Op0, m_Intrinsic())) { + cast(Op1)->setOperand(0, Op0); + I.setOperand(0, X); + return &I; + } else if (Op0->hasOneUse() && X->hasOneUse()) { + // Exact same case as WC & Wc2 above, except there's an extra condition + // (Y) involved. + return replaceInstUsesWith(I, Op1); + } + } + } return nullptr; } Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -402,6 +402,10 @@ II->getIntrinsicID() == Intrinsic::launder_invariant_group) return true; + // A widenable condition without uses is useless. + if (II->getIntrinsicID() == Intrinsic::experimental_widenable_condition) + return true; + // Lifetime intrinsics are dead when their right-hand is undef. if (II->isLifetimeStartOrEnd()) return isa(II->getArgOperand(1)); Index: llvm/test/Transforms/InstCombine/widenable-conditions.ll =================================================================== --- llvm/test/Transforms/InstCombine/widenable-conditions.ll +++ llvm/test/Transforms/InstCombine/widenable-conditions.ll @@ -4,11 +4,22 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" +define i1 @test0(i1 %a) { +; CHECK-LABEL: @test0( +; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[A:%.*]] +; CHECK-NEXT: ret i1 [[AND]] +; + %wc = call i1 @llvm.experimental.widenable.condition() + %and = and i1 %a, %wc + ret i1 %and +} + define i1 @test1(i1 %a, i1 %b) { ; CHECK-LABEL: @test1( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: [[LHS:%.*]] = and i1 [[WC]], [[B:%.*]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[A:%.*]] +; CHECK-NEXT: [[LHS:%.*]] = and i1 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[LHS]] ; CHECK-NEXT: ret i1 [[AND]] ; %wc = call i1 @llvm.experimental.widenable.condition() @@ -17,7 +28,7 @@ ret i1 %and } -; Negative test - profitability of dropping WC from first use unclear +; Negative test - %lhs and %and must remain correlated for any value of %wc define i1 @test1b(i1 %a, i1 %b) { ; CHECK-LABEL: @test1b( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() @@ -40,8 +51,8 @@ ; CHECK-NEXT: call void @use(i1 [[B:%.*]]) ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: call void @use(i1 [[WC]]) -; CHECK-NEXT: [[LHS:%.*]] = and i1 [[WC]], [[B]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[A]] +; CHECK-NEXT: [[LHS:%.*]] = and i1 [[A]], [[B]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[LHS]] ; CHECK-NEXT: ret i1 [[AND]] ; call void @use(i1 %a) @@ -56,8 +67,8 @@ define i1 @test2(i1 %a, i1 %b) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: [[LHS:%.*]] = and i1 [[WC]], [[B:%.*]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[A:%.*]] +; CHECK-NEXT: [[LHS:%.*]] = and i1 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[LHS]] ; CHECK-NEXT: ret i1 [[AND]] ; %wc = call i1 @llvm.experimental.widenable.condition() @@ -72,8 +83,8 @@ ; CHECK-LABEL: @test3( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[LHS:%.*]] = and i1 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[RHS:%.*]] = and i1 [[WC]], [[C:%.*]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[RHS]] +; CHECK-NEXT: [[RHS:%.*]] = and i1 [[LHS]], [[C:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[RHS]] ; CHECK-NEXT: ret i1 [[AND]] ; %wc = call i1 @llvm.experimental.widenable.condition() @@ -87,8 +98,8 @@ ; CHECK-LABEL: @test4( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[LHS:%.*]] = and i1 [[A:%.*]], [[B:%.*]] -; CHECK-NEXT: [[RHS:%.*]] = and i1 [[WC]], [[C:%.*]] -; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[RHS]] +; CHECK-NEXT: [[RHS:%.*]] = and i1 [[LHS]], [[C:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[RHS]] ; CHECK-NEXT: ret i1 [[AND]] ; %wc = call i1 @llvm.experimental.widenable.condition() @@ -98,7 +109,7 @@ ret i1 %and } -define i1 @test5(i1 %a, i1 %b) { +define i1 @test5() { ; CHECK-LABEL: @test5( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: ret i1 [[WC]] @@ -108,12 +119,10 @@ ret i1 %and } -define i1 @test6(i1 %a, i1 %b) { +define i1 @test6() { ; CHECK-LABEL: @test6( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: [[WC2:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[WC2]] -; CHECK-NEXT: ret i1 [[AND]] +; CHECK-NEXT: ret i1 [[WC]] ; %wc = call i1 @llvm.experimental.widenable.condition() %wc2 = call i1 @llvm.experimental.widenable.condition() @@ -121,8 +130,11 @@ ret i1 %and } -define i1 @test7(i1 %a, i1 %b) { -; CHECK-LABEL: @test7( +; It's tempting to pick the WC with more than one use, but that leaves this +; expression in a form which isn't (trivially) widenable. It's better to +; leave a single use WC as part of the expression. +define i1 @test6_neg1() { +; CHECK-LABEL: @test6_neg1( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: call void @use(i1 [[WC]]) ; CHECK-NEXT: [[WC2:%.*]] = call i1 @llvm.experimental.widenable.condition() @@ -136,18 +148,16 @@ ret i1 %and } -define i1 @test8(i1 %a, i1 %b) { +define i1 @test8(i1 %a) { ; CHECK-LABEL: @test8( ; CHECK-NEXT: [[WC:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: [[WC2:%.*]] = call i1 @llvm.experimental.widenable.condition() -; CHECK-NEXT: call void @use(i1 [[WC2]]) -; CHECK-NEXT: [[AND:%.*]] = and i1 [[WC]], [[WC2]] -; CHECK-NEXT: ret i1 [[AND]] +; CHECK-NEXT: [[RHS:%.*]] = and i1 [[WC]], [[A:%.*]] +; CHECK-NEXT: ret i1 [[RHS]] ; %wc = call i1 @llvm.experimental.widenable.condition() %wc2 = call i1 @llvm.experimental.widenable.condition() - call void @use(i1 %wc2) - %and = and i1 %wc, %wc2 + %rhs = and i1 %wc, %a + %and = and i1 %wc2, %rhs ret i1 %and }