Skip to content

Commit

Permalink
[InstCombine] Fold bitwise logic with intrinsics (llvm#77460)
Browse files Browse the repository at this point in the history
This patch does the following folds:
```
bitwise(fshl (A, B, ShAmt), fshl(C, D, ShAmt)) -> fshl(bitwise(A, C), bitwise(B, D), ShAmt)
bitwise(fshr (A, B, ShAmt), fshr(C, D, ShAmt)) -> fshr(bitwise(A, C), bitwise(B, D), ShAmt)
bitwise(bswap(A), bswap(B)) -> bswap(bitwise(A, B))
bitwise(bswap(A), C) -> bswap(bitwise(A, bswap(C)))
bitwise(bitreverse(A), bitreverse(B)) -> bitreverse(bitwise(A, B))
bitwise(bitreverse(A), C) -> bitreverse(bitwise(A, bitreverse(C)))
```
Alive2: https://alive2.llvm.org/ce/z/iZN_TL
  • Loading branch information
dtcxzyw authored Jan 10, 2024
1 parent 9e5a77f commit 29f98d6
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 54 deletions.
114 changes: 67 additions & 47 deletions llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,44 +46,6 @@ static Value *getFCmpValue(unsigned Code, Value *LHS, Value *RHS,
return Builder.CreateFCmp(NewPred, LHS, RHS);
}

/// Transform BITWISE_OP(BSWAP(A),BSWAP(B)) or
/// BITWISE_OP(BSWAP(A), Constant) to BSWAP(BITWISE_OP(A, B))
/// \param I Binary operator to transform.
/// \return Pointer to node that must replace the original binary operator, or
/// null pointer if no transformation was made.
static Value *SimplifyBSwap(BinaryOperator &I,
InstCombiner::BuilderTy &Builder) {
assert(I.isBitwiseLogicOp() && "Unexpected opcode for bswap simplifying");

Value *OldLHS = I.getOperand(0);
Value *OldRHS = I.getOperand(1);

Value *NewLHS;
if (!match(OldLHS, m_BSwap(m_Value(NewLHS))))
return nullptr;

Value *NewRHS;
const APInt *C;

if (match(OldRHS, m_BSwap(m_Value(NewRHS)))) {
// OP( BSWAP(x), BSWAP(y) ) -> BSWAP( OP(x, y) )
if (!OldLHS->hasOneUse() && !OldRHS->hasOneUse())
return nullptr;
// NewRHS initialized by the matcher.
} else if (match(OldRHS, m_APInt(C))) {
// OP( BSWAP(x), CONSTANT ) -> BSWAP( OP(x, BSWAP(CONSTANT) ) )
if (!OldLHS->hasOneUse())
return nullptr;
NewRHS = ConstantInt::get(I.getType(), C->byteSwap());
} else
return nullptr;

Value *BinOp = Builder.CreateBinOp(I.getOpcode(), NewLHS, NewRHS);
Function *F = Intrinsic::getDeclaration(I.getModule(), Intrinsic::bswap,
I.getType());
return Builder.CreateCall(F, BinOp);
}

/// Emit a computation of: (V >= Lo && V < Hi) if Inside is true, otherwise
/// (V < Lo || V >= Hi). This method expects that Lo < Hi. IsSigned indicates
/// whether to treat V, Lo, and Hi as signed or not.
Expand Down Expand Up @@ -2159,6 +2121,64 @@ Instruction *InstCombinerImpl::foldBinOpOfDisplacedShifts(BinaryOperator &I) {
return BinaryOperator::Create(ShiftOp, NewC, ShAmt);
}

// Fold and/or/xor with two equal intrinsic IDs:
// bitwise(fshl (A, B, ShAmt), fshl(C, D, ShAmt))
// -> fshl(bitwise(A, C), bitwise(B, D), ShAmt)
// bitwise(fshr (A, B, ShAmt), fshr(C, D, ShAmt))
// -> fshr(bitwise(A, C), bitwise(B, D), ShAmt)
// bitwise(bswap(A), bswap(B)) -> bswap(bitwise(A, B))
// bitwise(bswap(A), C) -> bswap(bitwise(A, bswap(C)))
// bitwise(bitreverse(A), bitreverse(B)) -> bitreverse(bitwise(A, B))
// bitwise(bitreverse(A), C) -> bitreverse(bitwise(A, bitreverse(C)))
static Instruction *
foldBitwiseLogicWithIntrinsics(BinaryOperator &I,
InstCombiner::BuilderTy &Builder) {
assert(I.isBitwiseLogicOp() && "Should and/or/xor");
if (!I.getOperand(0)->hasOneUse())
return nullptr;
IntrinsicInst *X = dyn_cast<IntrinsicInst>(I.getOperand(0));
if (!X)
return nullptr;

IntrinsicInst *Y = dyn_cast<IntrinsicInst>(I.getOperand(1));
if (Y && (!Y->hasOneUse() || X->getIntrinsicID() != Y->getIntrinsicID()))
return nullptr;

Intrinsic::ID IID = X->getIntrinsicID();
const APInt *RHSC;
// Try to match constant RHS.
if (!Y && (!(IID == Intrinsic::bswap || IID == Intrinsic::bitreverse) ||
!match(I.getOperand(1), m_APInt(RHSC))))
return nullptr;

switch (IID) {
case Intrinsic::fshl:
case Intrinsic::fshr: {
if (X->getOperand(2) != Y->getOperand(2))
return nullptr;
Value *NewOp0 =
Builder.CreateBinOp(I.getOpcode(), X->getOperand(0), Y->getOperand(0));
Value *NewOp1 =
Builder.CreateBinOp(I.getOpcode(), X->getOperand(1), Y->getOperand(1));
Function *F = Intrinsic::getDeclaration(I.getModule(), IID, I.getType());
return CallInst::Create(F, {NewOp0, NewOp1, X->getOperand(2)});
}
case Intrinsic::bswap:
case Intrinsic::bitreverse: {
Value *NewOp0 = Builder.CreateBinOp(
I.getOpcode(), X->getOperand(0),
Y ? Y->getOperand(0)
: ConstantInt::get(I.getType(), IID == Intrinsic::bswap
? RHSC->byteSwap()
: RHSC->reverseBits()));
Function *F = Intrinsic::getDeclaration(I.getModule(), IID, I.getType());
return CallInst::Create(F, {NewOp0});
}
default:
return nullptr;
}
}

// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
// here. We should standardize that construct where it is needed or choose some
// other way to ensure that commutated variants of patterns are not missed.
Expand Down Expand Up @@ -2194,9 +2214,6 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Value *V = foldUsingDistributiveLaws(I))
return replaceInstUsesWith(I, V);

if (Value *V = SimplifyBSwap(I, Builder))
return replaceInstUsesWith(I, V);

if (Instruction *R = foldBinOpShiftWithShift(I))
return R;

Expand Down Expand Up @@ -2688,6 +2705,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
return Res;

if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;

return nullptr;
}

Expand Down Expand Up @@ -3347,9 +3367,6 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (Value *V = foldUsingDistributiveLaws(I))
return replaceInstUsesWith(I, V);

if (Value *V = SimplifyBSwap(I, Builder))
return replaceInstUsesWith(I, V);

Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
Type *Ty = I.getType();
if (Ty->isIntOrIntVectorTy(1)) {
Expand Down Expand Up @@ -3884,6 +3901,9 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *C1 | *C2));
}

if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;

return nullptr;
}

Expand Down Expand Up @@ -4507,9 +4527,6 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (SimplifyDemandedInstructionBits(I))
return &I;

if (Value *V = SimplifyBSwap(I, Builder))
return replaceInstUsesWith(I, V);

if (Instruction *R = foldNot(I))
return R;

Expand Down Expand Up @@ -4799,5 +4816,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
return Res;

if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
return Res;

return nullptr;
}
5 changes: 2 additions & 3 deletions llvm/test/Transforms/InstCombine/bitreverse-known-bits.ll
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ define i1 @test3(i32 %arg) {

define i8 @add_bitreverse(i8 %a) {
; CHECK-LABEL: @add_bitreverse(
; CHECK-NEXT: [[B:%.*]] = and i8 [[A:%.*]], -4
; CHECK-NEXT: [[REVERSE:%.*]] = call i8 @llvm.bitreverse.i8(i8 [[B]]), !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = or disjoint i8 [[REVERSE]], -16
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[A:%.*]], 15
; CHECK-NEXT: [[C:%.*]] = call i8 @llvm.bitreverse.i8(i8 [[TMP1]])
; CHECK-NEXT: ret i8 [[C]]
;
%b = and i8 %a, 252
Expand Down
Loading

0 comments on commit 29f98d6

Please sign in to comment.