[llvm] 6def517 - [InstCombine] Fold `(X & Mask) == 0 ? TC : FC -> TC binop (X & Mask)` (#100437)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 6 07:04:27 PDT 2024
Author: Yingwei Zheng
Date: 2024-08-06T22:04:24+08:00
New Revision: 6def5170e80c0909206c87bdffa8c6e9e760f5c5
URL: https://github.com/llvm/llvm-project/commit/6def5170e80c0909206c87bdffa8c6e9e760f5c5
DIFF: https://github.com/llvm/llvm-project/commit/6def5170e80c0909206c87bdffa8c6e9e760f5c5.diff
LOG: [InstCombine] Fold `(X & Mask) == 0 ? TC : FC -> TC binop (X & Mask)` (#100437)
Alive2: https://alive2.llvm.org/ce/z/d9wV7N
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
llvm/test/Transforms/InstCombine/select-icmp-and.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index a22ee1de0ac21..39c185ee194dd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -155,42 +155,41 @@ static Value *foldSelectICmpAnd(SelectInst &Sel, ICmpInst *Cmp,
} else {
return nullptr;
}
+ if (Pred == ICmpInst::ICMP_NE)
+ std::swap(SelTC, SelFC);
// In general, when both constants are non-zero, we would need an offset to
// replace the select. This would require more instructions than we started
// with. But there's one special-case that we handle here because it can
// simplify/reduce the instructions.
- APInt TC = *SelTC;
- APInt FC = *SelFC;
+ const APInt &TC = *SelTC;
+ const APInt &FC = *SelFC;
if (!TC.isZero() && !FC.isZero()) {
- // If the select constants
diff er by exactly one bit and that's the same
- // bit that is masked and checked by the select condition, the select can
- // be replaced by bitwise logic to set/clear one bit of the constant result.
- if (TC.getBitWidth() != AndMask.getBitWidth() || (TC ^ FC) != AndMask)
+ if (TC.getBitWidth() != AndMask.getBitWidth())
return nullptr;
- if (CreateAnd) {
- // If we have to create an 'and', then we must kill the cmp to not
- // increase the instruction count.
- if (!Cmp->hasOneUse())
- return nullptr;
- V = Builder.CreateAnd(V, ConstantInt::get(SelType, AndMask));
- }
- bool ExtraBitInTC = TC.ugt(FC);
- if (Pred == ICmpInst::ICMP_EQ) {
- // If the masked bit in V is clear, clear or set the bit in the result:
- // (V & AndMaskC) == 0 ? TC : FC --> (V & AndMaskC) ^ TC
- // (V & AndMaskC) == 0 ? TC : FC --> (V & AndMaskC) | TC
- Constant *C = ConstantInt::get(SelType, TC);
- return ExtraBitInTC ? Builder.CreateXor(V, C) : Builder.CreateOr(V, C);
- }
- if (Pred == ICmpInst::ICMP_NE) {
- // If the masked bit in V is set, set or clear the bit in the result:
- // (V & AndMaskC) != 0 ? TC : FC --> (V & AndMaskC) | FC
- // (V & AndMaskC) != 0 ? TC : FC --> (V & AndMaskC) ^ FC
- Constant *C = ConstantInt::get(SelType, FC);
- return ExtraBitInTC ? Builder.CreateOr(V, C) : Builder.CreateXor(V, C);
+ // If we have to create an 'and', then we must kill the cmp to not
+ // increase the instruction count.
+ if (CreateAnd && !Cmp->hasOneUse())
+ return nullptr;
+
+ // (V & AndMaskC) == 0 ? TC : FC --> TC | (V & AndMaskC)
+ // (V & AndMaskC) == 0 ? TC : FC --> TC ^ (V & AndMaskC)
+ // (V & AndMaskC) == 0 ? TC : FC --> TC + (V & AndMaskC)
+ // (V & AndMaskC) == 0 ? TC : FC --> TC - (V & AndMaskC)
+ Constant *TCC = ConstantInt::get(SelType, TC);
+ Constant *FCC = ConstantInt::get(SelType, FC);
+ Constant *MaskC = ConstantInt::get(SelType, AndMask);
+ for (auto Opc : {Instruction::Or, Instruction::Xor, Instruction::Add,
+ Instruction::Sub}) {
+ if (ConstantFoldBinaryOpOperands(Opc, TCC, MaskC, Sel.getDataLayout()) ==
+ FCC) {
+ if (CreateAnd)
+ V = Builder.CreateAnd(V, MaskC);
+ return Builder.CreateBinOp(Opc, TCC, V);
+ }
}
- llvm_unreachable("Only expecting equality predicates");
+
+ return nullptr;
}
// Make sure one of the select arms is a power-of-2.
@@ -203,7 +202,6 @@ static Value *foldSelectICmpAnd(SelectInst &Sel, ICmpInst *Cmp,
unsigned ValZeros = ValC.logBase2();
unsigned AndZeros = AndMask.logBase2();
bool ShouldNotVal = !TC.isZero();
- ShouldNotVal ^= Pred == ICmpInst::ICMP_NE;
// If we would need to create an 'and' + 'shift' + 'xor' to replace a 'select'
// + 'icmp', then this transformation would result in more instructions and
diff --git a/llvm/test/Transforms/InstCombine/select-icmp-and.ll b/llvm/test/Transforms/InstCombine/select-icmp-and.ll
index 8bedf699dc922..a57a7c5e32e49 100644
--- a/llvm/test/Transforms/InstCombine/select-icmp-and.ll
+++ b/llvm/test/Transforms/InstCombine/select-icmp-and.ll
@@ -629,3 +629,89 @@ define i8 @set_to_clear_decomposebittest_extra_use(i8 %x) {
ret i8 %t3
}
+define i32 @select_bittest_to_add(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_add(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 1
+; CHECK-NEXT: [[RET:%.*]] = add nuw nsw i32 [[AND]], 3
+; CHECK-NEXT: ret i32 [[RET]]
+;
+entry:
+ %and = and i32 %x, 1
+ %cmp = icmp eq i32 %and, 0
+ %ret = select i1 %cmp, i32 3, i32 4
+ ret i32 %ret
+}
+
+define i32 @select_bittest_to_sub(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_sub(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 1
+; CHECK-NEXT: [[RET:%.*]] = sub nuw nsw i32 4, [[AND]]
+; CHECK-NEXT: ret i32 [[RET]]
+;
+entry:
+ %and = and i32 %x, 1
+ %cmp = icmp eq i32 %and, 0
+ %ret = select i1 %cmp, i32 4, i32 3
+ ret i32 %ret
+}
+
+define i32 @select_bittest_to_shl(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_shl(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i32 2, i32 4
+; CHECK-NEXT: ret i32 [[RET]]
+;
+entry:
+ %and = and i32 %x, 1
+ %cmp = icmp eq i32 %and, 0
+ %ret = select i1 %cmp, i32 2, i32 4
+ ret i32 %ret
+}
+
+define i32 @select_bittest_to_lshr(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_lshr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i32 4, i32 2
+; CHECK-NEXT: ret i32 [[RET]]
+;
+entry:
+ %and = and i32 %x, 1
+ %cmp = icmp eq i32 %and, 0
+ %ret = select i1 %cmp, i32 4, i32 2
+ ret i32 %ret
+}
+
+define i32 @select_bittest_to_ashr(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_ashr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 2
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i32 -4, i32 -1
+; CHECK-NEXT: ret i32 [[RET]]
+;
+entry:
+ %and = and i32 %x, 2
+ %cmp = icmp eq i32 %and, 0
+ %ret = select i1 %cmp, i32 -4, i32 -1
+ ret i32 %ret
+}
+
+define i32 @select_bittest_to_shl_negative_test(i32 %x) {
+; CHECK-LABEL: @select_bittest_to_shl_negative_test(
+; CHECK-NEXT: [[MASK:%.*]] = and i32 [[X:%.*]], 1
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[MASK]], 0
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i32 4, i32 6
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %mask = and i32 %x, 1
+ %cond = icmp eq i32 %mask, 0
+ %y = select i1 %cond, i32 2, i32 4
+ %res = add nuw nsw i32 %y, 2
+ ret i32 %res
+}
More information about the llvm-commits
mailing list