diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -14332,6 +14332,48 @@ return true; } + case Instruction::Or: { + // Pattern: Or(And(MaskValue, A), And(Not(MaskValue), B)) -> + // bitselect(MaskValue, A, B) where Not(MaskValue) = Xor(MaskValue, -1) + if (Subtarget->hasNEON()) { + // If any of the intermediate Ands have uses other than Or, don't fold + if (!I->getOperand(0)->hasOneUse() || !I->getOperand(0)->hasOneUse()) + return false; + + Instruction *OtherAnd, *IA, *IB; + Value *MaskValue; + // MainAnd refers to And instruction that has 'Not' as one of its operands + if (match(I, m_c_Or(m_Instruction(OtherAnd), + m_c_And(m_Not(m_Value(MaskValue)), + m_Instruction(IA))))) { + if (match(OtherAnd, + m_c_And(m_Specific(MaskValue), m_Instruction(IB)))) { + Instruction *MainAnd = I->getOperand(0) == OtherAnd + ? cast(I->getOperand(1)) + : cast(I->getOperand(0)); + + // Both Ands should be in same basic block as Or + if (I->getParent() != MainAnd->getParent() || + I->getParent() != OtherAnd->getParent()) + return false; + + // Non-mask operands of both Ands should also be in same basic block + if (I->getParent() != IA->getParent() || + I->getParent() != IB->getParent()) + return false; + + Ops.push_back(&MainAnd->getOperandUse(0)); + Ops.push_back(&MainAnd->getOperandUse(1)); + Ops.push_back(&I->getOperandUse(0)); + Ops.push_back(&I->getOperandUse(1)); + + return true; + } + } + } + + return false; + } case Instruction::Mul: { int NumZExts = 0, NumSExts = 0; for (auto &Op : I->operands()) {