diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 5ee3bb1abe86e..aac4a37f5640c 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -4229,26 +4229,33 @@ static Value *simplifySwitchOnSelectUsingRanges(SwitchInst &SI, Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) { Value *Cond = SI.getCondition(); Value *Op0; - ConstantInt *AddRHS; - if (match(Cond, m_Add(m_Value(Op0), m_ConstantInt(AddRHS)))) { - // Change 'switch (X+4) case 1:' into 'switch (X) case -3'. - for (auto Case : SI.cases()) { - Constant *NewCase = ConstantExpr::getSub(Case.getCaseValue(), AddRHS); - assert(isa(NewCase) && - "Result of expression should be constant"); - Case.setValue(cast(NewCase)); - } - return replaceOperand(SI, 0, Op0); - } + const APInt *CondOpC; + using InvertFn = std::function; - ConstantInt *SubLHS; - if (match(Cond, m_Sub(m_ConstantInt(SubLHS), m_Value(Op0)))) { - // Change 'switch (1-X) case 1:' into 'switch (X) case 0'. - for (auto Case : SI.cases()) { - Constant *NewCase = ConstantExpr::getSub(SubLHS, Case.getCaseValue()); - assert(isa(NewCase) && - "Result of expression should be constant"); - Case.setValue(cast(NewCase)); + auto MaybeInvertible = [&](Value *Cond) -> std::optional { + if (match(Cond, m_Add(m_Value(Op0), m_APInt(CondOpC)))) + // Change 'switch (X+C) case Case:' into 'switch (X) case Case-C'. + return [](const APInt &Case, const APInt &C) { return Case - C; }; + + if (match(Cond, m_Sub(m_APInt(CondOpC), m_Value(Op0)))) + // Change 'switch (C-X) case Case:' into 'switch (X) case C-Case'. + return [](const APInt &Case, const APInt &C) { return C - Case; }; + + if (match(Cond, m_Xor(m_Value(Op0), m_APInt(CondOpC))) && + !CondOpC->isMinSignedValue() && !CondOpC->isMaxSignedValue()) + // Change 'switch (X^C) case Case:' into 'switch (X) case Case^C'. + // Prevent creation of large case values by excluding extremes. + return [](const APInt &Case, const APInt &C) { return Case ^ C; }; + + return std::nullopt; + }; + + // Attempt to invert and simplify the switch condition, as long as the + // condition is not used further, as it may be unprofitable otherwise. + if (auto InvertFn = MaybeInvertible(Cond); InvertFn && Cond->hasOneUse()) { + for (auto &Case : SI.cases()) { + const APInt &New = (*InvertFn)(Case.getCaseValue()->getValue(), *CondOpC); + Case.setValue(ConstantInt::get(SI.getContext(), New)); } return replaceOperand(SI, 0, Op0); } diff --git a/llvm/test/Transforms/InstCombine/narrow-switch.ll b/llvm/test/Transforms/InstCombine/narrow-switch.ll index 90f56a61fa410..22859c90f8b78 100644 --- a/llvm/test/Transforms/InstCombine/narrow-switch.ll +++ b/llvm/test/Transforms/InstCombine/narrow-switch.ll @@ -235,11 +235,10 @@ define i32 @trunc32to16(i32 %a0) #0 { ; ALL-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 ; ALL-NEXT: [[XOR:%.*]] = lshr i32 [[A0]], 16 ; ALL-NEXT: [[TMP0:%.*]] = trunc nuw i32 [[XOR]] to i16 -; ALL-NEXT: [[TRUNC:%.*]] = xor i16 [[TMP0]], 15784 -; ALL-NEXT: switch i16 [[TRUNC]], label %[[SW_EPILOG:.*]] [ -; ALL-NEXT: i16 63, label %[[SW_BB:.*]] -; ALL-NEXT: i16 1, label %[[SW_BB1:.*]] -; ALL-NEXT: i16 100, label %[[SW_BB2:.*]] +; ALL-NEXT: switch i16 [[TMP0]], label %[[SW_EPILOG:.*]] [ +; ALL-NEXT: i16 15767, label %[[SW_BB:.*]] +; ALL-NEXT: i16 15785, label %[[SW_BB1:.*]] +; ALL-NEXT: i16 15820, label %[[SW_BB2:.*]] ; ALL-NEXT: ] ; ALL: [[SW_BB]]: ; ALL-NEXT: store i32 90, ptr [[RETVAL]], align 4 diff --git a/llvm/test/Transforms/InstCombine/switch-xor.ll b/llvm/test/Transforms/InstCombine/switch-xor.ll new file mode 100644 index 0000000000000..dce4c3e050cdb --- /dev/null +++ b/llvm/test/Transforms/InstCombine/switch-xor.ll @@ -0,0 +1,154 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i1 @test_switch_with_xor(i32 %x) { +; CHECK-LABEL: define i1 @test_switch_with_xor( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: switch i32 [[X]], label %[[SW_DEFAULT:.*]] [ +; CHECK-NEXT: i32 3, label %[[SW_BB:.*]] +; CHECK-NEXT: i32 0, label %[[SW_BB]] +; CHECK-NEXT: i32 1, label %[[SW_BB]] +; CHECK-NEXT: ] +; CHECK: [[SW_BB]]: +; CHECK-NEXT: ret i1 true +; CHECK: [[SW_DEFAULT]]: +; CHECK-NEXT: ret i1 false +; +entry: + %xor = xor i32 %x, 2 + switch i32 %xor, label %sw.default [ + i32 1, label %sw.bb + i32 2, label %sw.bb + i32 3, label %sw.bb + ] + +sw.bb: + ret i1 true +sw.default: + ret i1 false +} + +; Negative tests. + +define i1 @test_switch_with_xor_nonconstant_ops(i32 %x, i32 %y) { +; CHECK-LABEL: define i1 @test_switch_with_xor_nonconstant_ops( +; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X]], [[Y]] +; CHECK-NEXT: switch i32 [[XOR]], label %[[SW_DEFAULT:.*]] [ +; CHECK-NEXT: i32 1, label %[[SW_BB:.*]] +; CHECK-NEXT: i32 2, label %[[SW_BB]] +; CHECK-NEXT: i32 3, label %[[SW_BB]] +; CHECK-NEXT: ] +; CHECK: [[SW_BB]]: +; CHECK-NEXT: ret i1 true +; CHECK: [[SW_DEFAULT]]: +; CHECK-NEXT: ret i1 false +; +entry: + %xor = xor i32 %x, %y + switch i32 %xor, label %sw.default [ + i32 1, label %sw.bb + i32 2, label %sw.bb + i32 3, label %sw.bb + ] + +sw.bb: + ret i1 true +sw.default: + ret i1 false +} + +define i1 @test_switch_with_xor_condition_multiple_uses(i32 %x) { +; CHECK-LABEL: define i1 @test_switch_with_xor_condition_multiple_uses( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X]], 2 +; CHECK-NEXT: call void @opaque(i32 [[XOR]]) +; CHECK-NEXT: switch i32 [[XOR]], label %[[SW_DEFAULT:.*]] [ +; CHECK-NEXT: i32 1, label %[[SW_BB:.*]] +; CHECK-NEXT: i32 2, label %[[SW_BB]] +; CHECK-NEXT: i32 3, label %[[SW_BB]] +; CHECK-NEXT: ] +; CHECK: [[SW_BB]]: +; CHECK-NEXT: ret i1 true +; CHECK: [[SW_DEFAULT]]: +; CHECK-NEXT: ret i1 false +; +entry: + %xor = xor i32 %x, 2 + call void @opaque(i32 %xor) + switch i32 %xor, label %sw.default [ + i32 1, label %sw.bb + i32 2, label %sw.bb + i32 3, label %sw.bb + ] + +sw.bb: + ret i1 true +sw.default: + ret i1 false +} + +define i1 @test_switch_with_xor_by_int_min(i64 %x) { +; CHECK-LABEL: define i1 @test_switch_with_xor_by_int_min( +; CHECK-SAME: i64 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[XOR:%.*]] = xor i64 [[X]], -9223372036854775808 +; CHECK-NEXT: switch i64 [[XOR]], label %[[SW_DEFAULT:.*]] [ +; CHECK-NEXT: i64 1, label %[[SW_BB:.*]] +; CHECK-NEXT: i64 2, label %[[SW_BB]] +; CHECK-NEXT: i64 3, label %[[SW_BB]] +; CHECK-NEXT: ] +; CHECK: [[SW_BB]]: +; CHECK-NEXT: ret i1 true +; CHECK: [[SW_DEFAULT]]: +; CHECK-NEXT: ret i1 false +; +entry: + %xor = xor i64 %x, -9223372036854775808 + switch i64 %xor, label %sw.default [ + i64 1, label %sw.bb + i64 2, label %sw.bb + i64 3, label %sw.bb + ] + +sw.bb: ; preds = %entry, %entry, %entry + ret i1 true + +sw.default: ; preds = %entry + ret i1 false +} + +define i1 @test_switch_with_xor_by_int_max(i64 %x) { +; CHECK-LABEL: define i1 @test_switch_with_xor_by_int_max( +; CHECK-SAME: i64 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[XOR:%.*]] = xor i64 [[X]], 9223372036854775807 +; CHECK-NEXT: switch i64 [[XOR]], label %[[SW_DEFAULT:.*]] [ +; CHECK-NEXT: i64 1, label %[[SW_BB:.*]] +; CHECK-NEXT: i64 2, label %[[SW_BB]] +; CHECK-NEXT: i64 3, label %[[SW_BB]] +; CHECK-NEXT: ] +; CHECK: [[SW_BB]]: +; CHECK-NEXT: ret i1 true +; CHECK: [[SW_DEFAULT]]: +; CHECK-NEXT: ret i1 false +; +entry: + %xor = xor i64 %x, 9223372036854775807 + switch i64 %xor, label %sw.default [ + i64 1, label %sw.bb + i64 2, label %sw.bb + i64 3, label %sw.bb + ] + +sw.bb: ; preds = %entry, %entry, %entry + ret i1 true + +sw.default: ; preds = %entry + ret i1 false +} + +declare void @opaque(i32)