Index: llvm/lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -14891,6 +14891,62 @@ return DAG.getNode(AArch64ISD::CSINC, DL, VT, NewNode, RHS, CCVal, Cmp); } +/// Perform the scalar expression combine in the form of: +/// CSNEG(1, c, cc) + b => CSINC(b-c, b, !cc) +/// CSNEG(c, -1, cc) + b => CSINC(b+c, b, cc) +static SDValue performAddCSnegIntoCSinc(SDNode *N, SelectionDAG &DAG) { + EVT VT = N->getValueType(0); + if (!VT.isScalarInteger() || N->getOpcode() != ISD::ADD) + return SDValue(); + + SDValue CSneg = N->getOperand(0); + SDValue RHS = N->getOperand(1); + + // Handle commutivity. + if (CSneg.getOpcode() != AArch64ISD::CSNEG) { + std::swap(CSneg, RHS); + if (CSneg.getOpcode() != AArch64ISD::CSNEG) { + return SDValue(); + } + } + + if (!CSneg.hasOneUse()) + return SDValue(); + + AArch64CC::CondCode AArch64CC = + static_cast(CSneg.getConstantOperandVal(2)); + + // The CSNEG should include One or NegOne operand. + ConstantSDNode *CTVal = dyn_cast(CSneg.getOperand(0)); + ConstantSDNode *CFVal = dyn_cast(CSneg.getOperand(1)); + if (!CTVal || !CFVal || (!CTVal->isOne() && !CFVal->isAllOnes())) + return SDValue(); + + SDLoc DL(N); + // Switch CSNEG(1, c, cc) to CSNEG(-c, -1, !cc) + if (CTVal->isOne() && !CFVal->isAllOnes()) { + int64_t C = -1 * CFVal->getSExtValue(); + CTVal = cast(DAG.getConstant(C, DL, VT)); + CFVal = cast(DAG.getConstant(-1, DL, VT)); + AArch64CC = AArch64CC::getInvertedCondCode(AArch64CC); + } + + // It might be neutral for larger constants, as the immediate need to be + // materialized in a register. + APInt ADDC = CTVal->getAPIntValue(); + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (!TLI.isLegalAddImmediate(ADDC.getSExtValue())) + return SDValue(); + + assert(CFVal->isAllOnes() && "Unexpected constant value"); + + SDValue NewNode = DAG.getNode(ISD::SUB, DL, VT, RHS, SDValue(CTVal, 0)); + SDValue CCVal = DAG.getConstant(AArch64CC, DL, MVT::i32); + SDValue Cmp = CSneg.getOperand(3); + + return DAG.getNode(AArch64ISD::CSINC, DL, VT, NewNode, RHS, CCVal, Cmp); +} + // ADD(UDOT(zero, x, y), A) --> UDOT(A, x, y) static SDValue performAddDotCombine(SDNode *N, SelectionDAG &DAG) { EVT VT = N->getValueType(0); @@ -14977,6 +15033,8 @@ return Val; if (SDValue Val = performAddCSelIntoCSinc(N, DAG)) return Val; + if (SDValue Val = performAddCSnegIntoCSinc(N, DAG)) + return Val; return performAddSubLongCombine(N, DCI, DAG); } Index: llvm/test/CodeGen/AArch64/aarch64-isel-csinc.ll =================================================================== --- llvm/test/CodeGen/AArch64/aarch64-isel-csinc.ll +++ llvm/test/CodeGen/AArch64/aarch64-isel-csinc.ll @@ -112,3 +112,33 @@ %cond = add nsw i32 %cond.v, %b ret i32 %cond } + +; int csinc8 (int a, int b) { return a ? b-1 : b+1; } +define dso_local i32 @csinc8(i32 %a, i32 %b) { +; CHECK-LABEL: csinc8: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: cmp w0, #0 +; CHECK-NEXT: add w8, w1, #1 +; CHECK-NEXT: csinc w0, w8, w1, ne +; CHECK-NEXT: ret +entry: + %tobool.not = icmp eq i32 %a, 0 + %cond.v = select i1 %tobool.not, i32 1, i32 -1 + %cond = add nsw i32 %cond.v, %b + ret i32 %cond +} + +; int csinc9 (int a, int b) { return a ? b+1 : b-1; } +define dso_local i32 @csinc9(i32 %a, i32 %b) { +; CHECK-LABEL: csinc9: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: cmp w0, #0 +; CHECK-NEXT: add w8, w1, #1 +; CHECK-NEXT: csinc w0, w8, w1, eq +; CHECK-NEXT: ret +entry: + %tobool.not = icmp eq i32 %a, 0 + %cond.v = select i1 %tobool.not, i32 -1, i32 1 + %cond = add nsw i32 %cond.v, %b + ret i32 %cond +}