Skip to content

Commit

Permalink
[InstCombine] Fold (X & Mask) == 0 ? TC : FC -> TC binop (X & Mask) (
Browse files Browse the repository at this point in the history
  • Loading branch information
dtcxzyw authored Aug 6, 2024
1 parent 2499978 commit 6def517
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 29 deletions.
56 changes: 27 additions & 29 deletions llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 differ 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.
Expand All @@ -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
Expand Down
86 changes: 86 additions & 0 deletions llvm/test/Transforms/InstCombine/select-icmp-and.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 6def517

Please sign in to comment.