[InstCombine] use select-of-constants with set/clear bit mask patterns

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
This commit is contained in:
Sanjay Patel 2020-05-03 09:43:49 -04:00
parent af28c74e8f
commit 682f0b366b
3 changed files with 54 additions and 11 deletions

View File

@ -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.
@ -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);

View File

@ -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]]
;

View File

@ -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
@ -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
@ -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
@ -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
@ -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>
@ -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>
@ -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>
@ -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