forked from OSchip/llvm-project
[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:
parent
af28c74e8f
commit
682f0b366b
|
@ -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);
|
||||
|
|
|
@ -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]]
|
||||
;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue