Skip to content

Commit 3933698

Browse files
[InstCombine] Canonicalize switch(X^C) to switch(X)
`switch(X^C)` expressions can be folded to `switch(X)`. Minor opportunity to generalize simplifications in `visitSwitchInst` via an inverse function helper as well. Proof: https://alive2.llvm.org/ce/z/TMRy_3.
1 parent c13a979 commit 3933698

File tree

3 files changed

+30
-29
lines changed

3 files changed

+30
-29
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3878,26 +3878,29 @@ static Value *simplifySwitchOnSelectUsingRanges(SwitchInst &SI,
38783878
Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
38793879
Value *Cond = SI.getCondition();
38803880
Value *Op0;
3881-
ConstantInt *AddRHS;
3882-
if (match(Cond, m_Add(m_Value(Op0), m_ConstantInt(AddRHS)))) {
3883-
// Change 'switch (X+4) case 1:' into 'switch (X) case -3'.
3884-
for (auto Case : SI.cases()) {
3885-
Constant *NewCase = ConstantExpr::getSub(Case.getCaseValue(), AddRHS);
3886-
assert(isa<ConstantInt>(NewCase) &&
3887-
"Result of expression should be constant");
3888-
Case.setValue(cast<ConstantInt>(NewCase));
3889-
}
3890-
return replaceOperand(SI, 0, Op0);
3891-
}
3881+
const APInt *CondOpC;
3882+
using InvertFn = std::function<APInt(const APInt &Case, const APInt &C)>;
38923883

3893-
ConstantInt *SubLHS;
3894-
if (match(Cond, m_Sub(m_ConstantInt(SubLHS), m_Value(Op0)))) {
3895-
// Change 'switch (1-X) case 1:' into 'switch (X) case 0'.
3896-
for (auto Case : SI.cases()) {
3897-
Constant *NewCase = ConstantExpr::getSub(SubLHS, Case.getCaseValue());
3898-
assert(isa<ConstantInt>(NewCase) &&
3899-
"Result of expression should be constant");
3900-
Case.setValue(cast<ConstantInt>(NewCase));
3884+
auto MaybeInvertible = [&](Value *Cond) -> std::optional<InvertFn> {
3885+
if (match(Cond, m_Add(m_Value(Op0), m_APInt(CondOpC))))
3886+
// Change 'switch (X+C) case A:' into 'switch (X) case C-A'.
3887+
return [](const APInt &C, const APInt &Case) { return C - Case; };
3888+
3889+
if (match(Cond, m_Sub(m_APInt(CondOpC), m_Value(Op0))))
3890+
// Change 'switch (C-X) case A:' into 'switch (X) case A-C'.
3891+
return [](const APInt &C, const APInt &Case) { return Case - C; };
3892+
3893+
if (match(Cond, m_Xor(m_Value(Op0), m_APInt(CondOpC))))
3894+
// Change 'switch (X^C) case A:' into 'switch (X) case A^C'.
3895+
return [](const APInt &C, const APInt &Case) { return C ^ Case; };
3896+
3897+
return std::nullopt;
3898+
};
3899+
3900+
if (auto InvertFn = MaybeInvertible(Cond)) {
3901+
for (auto &Case : SI.cases()) {
3902+
const APInt &New = (*InvertFn)(Case.getCaseValue()->getValue(), *CondOpC);
3903+
Case.setValue(ConstantInt::get(SI.getContext(), New));
39013904
}
39023905
return replaceOperand(SI, 0, Op0);
39033906
}

llvm/test/Transforms/InstCombine/narrow-switch.ll

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,10 @@ define i32 @trunc32to16(i32 %a0) #0 {
235235
; ALL-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
236236
; ALL-NEXT: [[XOR:%.*]] = lshr i32 [[A0]], 16
237237
; ALL-NEXT: [[TMP0:%.*]] = trunc nuw i32 [[XOR]] to i16
238-
; ALL-NEXT: [[TRUNC:%.*]] = xor i16 [[TMP0]], 15784
239-
; ALL-NEXT: switch i16 [[TRUNC]], label %[[SW_EPILOG:.*]] [
240-
; ALL-NEXT: i16 63, label %[[SW_BB:.*]]
241-
; ALL-NEXT: i16 1, label %[[SW_BB1:.*]]
242-
; ALL-NEXT: i16 100, label %[[SW_BB2:.*]]
238+
; ALL-NEXT: switch i16 [[TMP0]], label %[[SW_EPILOG:.*]] [
239+
; ALL-NEXT: i16 15767, label %[[SW_BB:.*]]
240+
; ALL-NEXT: i16 15785, label %[[SW_BB1:.*]]
241+
; ALL-NEXT: i16 15820, label %[[SW_BB2:.*]]
243242
; ALL-NEXT: ]
244243
; ALL: [[SW_BB]]:
245244
; ALL-NEXT: store i32 90, ptr [[RETVAL]], align 4

llvm/test/Transforms/InstCombine/switch-xor.ll

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ define i1 @test_switch_with_xor(i32 %x) {
55
; CHECK-LABEL: define i1 @test_switch_with_xor(
66
; CHECK-SAME: i32 [[X:%.*]]) {
77
; CHECK-NEXT: [[ENTRY:.*:]]
8-
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X]], 2
9-
; CHECK-NEXT: switch i32 [[XOR]], label %[[SW_DEFAULT:.*]] [
10-
; CHECK-NEXT: i32 1, label %[[SW_BB:.*]]
11-
; CHECK-NEXT: i32 2, label %[[SW_BB]]
12-
; CHECK-NEXT: i32 3, label %[[SW_BB]]
8+
; CHECK-NEXT: switch i32 [[X]], label %[[SW_DEFAULT:.*]] [
9+
; CHECK-NEXT: i32 3, label %[[SW_BB:.*]]
10+
; CHECK-NEXT: i32 0, label %[[SW_BB]]
11+
; CHECK-NEXT: i32 1, label %[[SW_BB]]
1312
; CHECK-NEXT: ]
1413
; CHECK: [[SW_BB]]:
1514
; CHECK-NEXT: ret i1 true

0 commit comments

Comments
 (0)