Skip to content

Commit

Permalink
[InstCombine] use select-of-constants with set/clear bit mask patterns
Browse files Browse the repository at this point in the history
Cond ? (X & ~C) : (X | C) --> (X & ~C) | (Cond ? 0 : C)
Cond ? (X | C) : (X & ~C) --> (X & ~C) | (Cond ? C : 0)

The select-of-constants form results in better codegen.
There's an existing test diff that shows a transform that
results in an extra IR instruction, but that's an existing
problem.

This is motivated by code seen in LLVM itself - see PR37581:
https://bugs.llvm.org/show_bug.cgi?id=37581

define i8 @src(i8 %x, i8 %C, i1 %b)  {
  %notC = xor i8 %C, -1
  %and = and i8 %x, %notC
  %or = or i8 %x, %C
  %cond = select i1 %b, i8 %or, i8 %and
  ret i8 %cond
}

define i8 @tgt(i8 %x, i8 %C, i1 %b)  {
  %notC = xor i8 %C, -1
  %and = and i8 %x, %notC
  %mul = select i1 %b, i8 %C, i8 0
  %or = or i8 %mul, %and
  ret i8 %or
}

http://volta.cs.utah.edu:8080/z/Vt2WVm

Differential Revision: https://reviews.llvm.org/D78880
  • Loading branch information
rotateright committed May 3, 2020
1 parent af28c74 commit 682f0b3
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 11 deletions.
34 changes: 34 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
Expand Up @@ -671,6 +671,38 @@ static Value *foldSelectICmpAndOr(const ICmpInst *IC, Value *TrueVal,
return Builder.CreateOr(V, Y);
}

/// Canonicalize a set or clear of a masked set of constant bits to
/// select-of-constants form.
static Instruction *foldSetClearBits(SelectInst &Sel,
InstCombiner::BuilderTy &Builder) {
Value *Cond = Sel.getCondition();
Value *T = Sel.getTrueValue();
Value *F = Sel.getFalseValue();
Type *Ty = Sel.getType();
Value *X;
const APInt *NotC, *C;

// Cond ? (X & ~C) : (X | C) --> (X & ~C) | (Cond ? 0 : C)
if (match(T, m_And(m_Value(X), m_APInt(NotC))) &&
match(F, m_OneUse(m_Or(m_Specific(X), m_APInt(C)))) && *NotC == ~(*C)) {
Constant *Zero = ConstantInt::getNullValue(Ty);
Constant *OrC = ConstantInt::get(Ty, *C);
Value *NewSel = Builder.CreateSelect(Cond, Zero, OrC, "masksel", &Sel);
return BinaryOperator::CreateOr(T, NewSel);
}

// Cond ? (X | C) : (X & ~C) --> (X & ~C) | (Cond ? C : 0)
if (match(F, m_And(m_Value(X), m_APInt(NotC))) &&
match(T, m_OneUse(m_Or(m_Specific(X), m_APInt(C)))) && *NotC == ~(*C)) {
Constant *Zero = ConstantInt::getNullValue(Ty);
Constant *OrC = ConstantInt::get(Ty, *C);
Value *NewSel = Builder.CreateSelect(Cond, OrC, Zero, "masksel", &Sel);
return BinaryOperator::CreateOr(F, NewSel);
}

return nullptr;
}

/// Transform patterns such as (a > b) ? a - b : 0 into usub.sat(a, b).
/// There are 8 commuted/swapped variants of this pattern.
/// TODO: Also support a - UMIN(a,b) patterns.
Expand Down Expand Up @@ -2553,6 +2585,8 @@ Instruction *InstCombiner::visitSelectInst(SelectInst &SI) {
return Add;
if (Instruction *Add = foldOverflowingAddSubSelect(SI, Builder))
return Add;
if (Instruction *Or = foldSetClearBits(SI, Builder))
return Or;

// Turn (select C, (op X, Y), (op X, Z)) -> (op X, (select C, Y, Z))
auto *TI = dyn_cast<Instruction>(TrueVal);
Expand Down
7 changes: 4 additions & 3 deletions llvm/test/Transforms/InstCombine/cast.ll
Expand Up @@ -666,9 +666,10 @@ define i64 @test50(i64 %x) {
define i64 @test51(i64 %A, i1 %cond) {
; ALL-LABEL: @test51(
; ALL-NEXT: [[C:%.*]] = and i64 [[A:%.*]], 4294967294
; ALL-NEXT: [[D:%.*]] = or i64 [[A]], 1
; ALL-NEXT: [[E:%.*]] = select i1 [[COND:%.*]], i64 [[C]], i64 [[D]]
; ALL-NEXT: [[SEXT:%.*]] = shl i64 [[E]], 32
; ALL-NEXT: [[NOT_COND:%.*]] = xor i1 [[COND:%.*]], true
; ALL-NEXT: [[MASKSEL:%.*]] = zext i1 [[NOT_COND]] to i64
; ALL-NEXT: [[E:%.*]] = or i64 [[C]], [[MASKSEL]]
; ALL-NEXT: [[SEXT:%.*]] = shl nuw i64 [[E]], 32
; ALL-NEXT: [[F:%.*]] = ashr exact i64 [[SEXT]], 32
; ALL-NEXT: ret i64 [[F]]
;
Expand Down
24 changes: 16 additions & 8 deletions llvm/test/Transforms/InstCombine/select-with-bitwise-ops.ll
Expand Up @@ -1456,8 +1456,8 @@ define i32 @shift_xor_multiuse_cmp_and(i32 %x, i32 %y, i32 %z, i32 %w) {
define i8 @set_bits(i8 %x, i1 %b) {
; CHECK-LABEL: @set_bits(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6
; CHECK-NEXT: [[OR:%.*]] = or i8 [[X]], 5
; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], i8 [[OR]], i8 [[AND]]
; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], i8 5, i8 0
; CHECK-NEXT: [[COND:%.*]] = or i8 [[AND]], [[MASKSEL]]
; CHECK-NEXT: ret i8 [[COND]]
;
%and = and i8 %x, 250
Expand All @@ -1466,6 +1466,8 @@ define i8 @set_bits(i8 %x, i1 %b) {
ret i8 %cond
}

; Negative test

define i8 @set_bits_not_inverse_constant(i8 %x, i1 %b) {
; CHECK-LABEL: @set_bits_not_inverse_constant(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6
Expand All @@ -1483,8 +1485,8 @@ define i8 @set_bits_extra_use1(i8 %x, i1 %b) {
; CHECK-LABEL: @set_bits_extra_use1(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6
; CHECK-NEXT: call void @use(i8 [[AND]])
; CHECK-NEXT: [[OR:%.*]] = or i8 [[X]], 5
; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], i8 [[OR]], i8 [[AND]]
; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], i8 5, i8 0
; CHECK-NEXT: [[COND:%.*]] = or i8 [[AND]], [[MASKSEL]]
; CHECK-NEXT: ret i8 [[COND]]
;
%and = and i8 %x, 250
Expand All @@ -1494,6 +1496,8 @@ define i8 @set_bits_extra_use1(i8 %x, i1 %b) {
ret i8 %cond
}

; Negative test

define i8 @set_bits_extra_use2(i8 %x, i1 %b) {
; CHECK-LABEL: @set_bits_extra_use2(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6
Expand All @@ -1512,8 +1516,8 @@ define i8 @set_bits_extra_use2(i8 %x, i1 %b) {
define <2 x i8> @clear_bits(<2 x i8> %x, <2 x i1> %b) {
; CHECK-LABEL: @clear_bits(
; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], <i8 37, i8 37>
; CHECK-NEXT: [[OR:%.*]] = or <2 x i8> [[X]], <i8 -38, i8 -38>
; CHECK-NEXT: [[COND:%.*]] = select <2 x i1> [[B:%.*]], <2 x i8> [[AND]], <2 x i8> [[OR]]
; CHECK-NEXT: [[MASKSEL:%.*]] = select <2 x i1> [[B:%.*]], <2 x i8> zeroinitializer, <2 x i8> <i8 -38, i8 -38>
; CHECK-NEXT: [[COND:%.*]] = or <2 x i8> [[AND]], [[MASKSEL]]
; CHECK-NEXT: ret <2 x i8> [[COND]]
;
%and = and <2 x i8> %x, <i8 37, i8 37>
Expand All @@ -1522,6 +1526,8 @@ define <2 x i8> @clear_bits(<2 x i8> %x, <2 x i1> %b) {
ret <2 x i8> %cond
}

; Negative test

define <2 x i8> @clear_bits_not_inverse_constant(<2 x i8> %x, <2 x i1> %b) {
; CHECK-LABEL: @clear_bits_not_inverse_constant(
; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], <i8 undef, i8 37>
Expand All @@ -1539,8 +1545,8 @@ define <2 x i8> @clear_bits_extra_use1(<2 x i8> %x, i1 %b) {
; CHECK-LABEL: @clear_bits_extra_use1(
; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], <i8 37, i8 37>
; CHECK-NEXT: call void @use_vec(<2 x i8> [[AND]])
; CHECK-NEXT: [[OR:%.*]] = or <2 x i8> [[X]], <i8 -38, i8 -38>
; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], <2 x i8> [[AND]], <2 x i8> [[OR]]
; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], <2 x i8> zeroinitializer, <2 x i8> <i8 -38, i8 -38>
; CHECK-NEXT: [[COND:%.*]] = or <2 x i8> [[AND]], [[MASKSEL]]
; CHECK-NEXT: ret <2 x i8> [[COND]]
;
%and = and <2 x i8> %x, <i8 37, i8 37>
Expand All @@ -1550,6 +1556,8 @@ define <2 x i8> @clear_bits_extra_use1(<2 x i8> %x, i1 %b) {
ret <2 x i8> %cond
}

; Negative test

define i8 @clear_bits_extra_use2(i8 %x, i1 %b) {
; CHECK-LABEL: @clear_bits_extra_use2(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6
Expand Down

0 comments on commit 682f0b3

Please sign in to comment.