Index: llvm/lib/Target/AArch64/AArch64ISelLowering.h =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -153,6 +153,9 @@ SBCS, ANDS, + // Scalar logical instructions with not operand + SBIC, + // Conditional compares. Operands: left,right,falsecc,cc,flags CCMP, CCMN, Index: llvm/lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -2263,6 +2263,7 @@ MAKE_CASE(AArch64ISD::ADCS) MAKE_CASE(AArch64ISD::SBCS) MAKE_CASE(AArch64ISD::ANDS) + MAKE_CASE(AArch64ISD::SBIC) MAKE_CASE(AArch64ISD::CCMP) MAKE_CASE(AArch64ISD::CCMN) MAKE_CASE(AArch64ISD::FCCMP) @@ -16042,6 +16043,61 @@ return SDValue(); } +// (~X | C) & Y --> bic Y, (X & ~C) +static SDValue performAndCombineWithNotOp(SDNode *N, SelectionDAG &DAG) { + EVT VT = N->getValueType(0); + if (VT != MVT::i32 && VT != MVT::i64) + return SDValue(); + + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + + SDLoc DL(N); + SDValue X, C; + auto canGetNotFromOrXor = [&](SDNode *N, SDValue Op0) { + if (Op0.getOpcode() != ISD::OR || !Op0.hasOneUse()) + return false; + + if (!isa(Op0.getOperand(1))) + return false; + + SDValue Op0LHS = Op0.getOperand(0); + if (!Op0LHS.hasOneUse()) + return false; + + if (Op0LHS->getOpcode() == ISD::ANY_EXTEND && VT == MVT::i64 && + Op0LHS->getOperand(0).getValueType() == MVT::i32) + Op0LHS = Op0LHS->getOperand(0); + + if (Op0LHS.getOpcode() != ISD::XOR || !Op0LHS.hasOneUse()) + return false; + + ConstantSDNode *XorC = dyn_cast(Op0LHS.getOperand(1)); + if (!XorC || !XorC->isAllOnes()) + return false; + + C = DAG.getNOT(DL, Op0.getOperand(1), VT); + X = Op0LHS.getOperand(0); + return true; + }; + + if (canGetNotFromOrXor(N, LHS) && !isa(RHS)) { + if (X.getValueType() != VT) + X = DAG.getNode(ISD::ANY_EXTEND, DL, VT, X); + SDValue AndVal = DAG.getNode(ISD::AND, DL, VT, X, C); + return DAG.getNode(AArch64ISD::SBIC, DL, VT, RHS, AndVal); + } + + if (canGetNotFromOrXor(N, RHS)) { + if (X.getValueType() != VT) + X = DAG.getNode(ISD::ANY_EXTEND, DL, VT, X); + SDValue AndVal = DAG.getNode(ISD::AND, DL, VT, X, C); + return DAG.getNode(AArch64ISD::SBIC, DL, VT, LHS, AndVal); + } + + return SDValue(); +} + static SDValue performANDCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { SelectionDAG &DAG = DCI.DAG; @@ -16058,6 +16114,9 @@ if (VT.isScalableVector()) return performSVEAndCombine(N, DCI); + if (SDValue R = performAndCombineWithNotOp(N, DAG)) + return R; + // The combining code below works only for NEON vectors. In particular, it // does not work for SVE when dealing with vectors wider than 128 bits. if (!VT.is64BitVector() && !VT.is128BitVector()) Index: llvm/lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -613,6 +613,8 @@ def AArch64ccmn : SDNode<"AArch64ISD::CCMN", SDT_AArch64CCMP>; def AArch64fccmp : SDNode<"AArch64ISD::FCCMP", SDT_AArch64FCCMP>; +def AArch64sbic : SDNode<"AArch64ISD::SBIC", SDTIntBinOp>; + def AArch64threadpointer : SDNode<"AArch64ISD::THREAD_POINTER", SDTPtrLeaf>; def AArch64fcmp : SDNode<"AArch64ISD::FCMP", SDT_AArch64FCmp>; @@ -2219,6 +2221,26 @@ def : Pat<(not GPR32:$Wm), (ORNWrr WZR, GPR32:$Wm)>; def : Pat<(not GPR64:$Xm), (ORNXrr XZR, GPR64:$Xm)>; +multiclass LogicalRegPat { + def : Pat<(OpNode GPR32:$Wn, GPR32:$Wm), + (!cast(INST # Wrr) GPR32:$Wn, GPR32:$Wm)>; + + def : Pat<(OpNode GPR64:$Xn, GPR64:$Xm), + (!cast(INST # Xrr) GPR64:$Xn, GPR64:$Xm)>; + + def : Pat<(OpNode GPR32:$Wn, + (logical_shifted_reg32 GPR32:$Wm, logical_shift32:$shift)), + (!cast(INST # Wrs) GPR32:$Wn, GPR32:$Wm, + logical_shift32:$shift)>; + + def : Pat<(OpNode GPR64:$Xn, + (logical_shifted_reg64 GPR64:$Xm, logical_shift64:$shift)), + (!cast(INST # Xrs) GPR64:$Xn, GPR64:$Xm, + logical_shift64:$shift)>; +} + +defm : LogicalRegPat; + //===----------------------------------------------------------------------===// // One operand data processing instructions. Index: llvm/test/CodeGen/AArch64/logical-op-with-not.ll =================================================================== --- llvm/test/CodeGen/AArch64/logical-op-with-not.ll +++ llvm/test/CodeGen/AArch64/logical-op-with-not.ll @@ -4,9 +4,8 @@ define i64 @and_bic(i64 %0, i64 %1) { ; CHECK-LABEL: and_bic: ; CHECK: // %bb.0: -; CHECK-NEXT: mvn w8, w0 -; CHECK-NEXT: orr x8, x8, #0xffffffffffff00ff -; CHECK-NEXT: and x0, x8, x1 +; CHECK-NEXT: and x8, x0, #0xff00 +; CHECK-NEXT: bic x0, x1, x8 ; CHECK-NEXT: ret %3 = and i64 %0, 65280 %4 = xor i64 %3, -1 @@ -31,9 +30,8 @@ define i32 @and_bic3(i32 %0, i32 %1) { ; CHECK-LABEL: and_bic3: ; CHECK: // %bb.0: -; CHECK-NEXT: mvn w8, w0 -; CHECK-NEXT: orr w8, w8, #0xffff00ff -; CHECK-NEXT: and w0, w8, w1 +; CHECK-NEXT: and w8, w0, #0xff00 +; CHECK-NEXT: bic w0, w1, w8 ; CHECK-NEXT: ret %3 = and i32 %0, 65280 %4 = xor i32 %3, -1 Index: llvm/test/CodeGen/AArch64/shiftregister-from-and.ll =================================================================== --- llvm/test/CodeGen/AArch64/shiftregister-from-and.ll +++ llvm/test/CodeGen/AArch64/shiftregister-from-and.ll @@ -16,14 +16,11 @@ ret i64 %r } -; TODO: logic shift reg pattern: bic - define i64 @bic_shiftedreg_from_and(i64 %a, i64 %b) { ; CHECK-LABEL: bic_shiftedreg_from_and: ; CHECK: // %bb.0: -; CHECK-NEXT: mov w8, #16777215 -; CHECK-NEXT: orn x8, x8, x0, asr #23 -; CHECK-NEXT: and x0, x1, x8 +; CHECK-NEXT: asr x8, x0, #47 +; CHECK-NEXT: bic x0, x1, x8, lsl #24 ; CHECK-NEXT: ret %ashr = ashr i64 %a, 23 %and = and i64 %ashr, -16777216