Index: lib/Target/ARM/ARMISelLowering.h =================================================================== --- lib/Target/ARM/ARMISelLowering.h +++ lib/Target/ARM/ARMISelLowering.h @@ -616,7 +616,8 @@ SDValue LowerGlobalTLSAddressWindows(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGLOBAL_OFFSET_TABLE(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerXALUO(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSignedALUO(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerUnsignedALUO(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -801,6 +801,9 @@ setOperationAction(ISD::SSUBO, MVT::i32, Custom); setOperationAction(ISD::USUBO, MVT::i32, Custom); + setOperationAction(ISD::ADDCARRY, MVT::i32, Custom); + setOperationAction(ISD::SUBCARRY, MVT::i32, Custom); + // i64 operation support. setOperationAction(ISD::MUL, MVT::i64, Expand); setOperationAction(ISD::MULHU, MVT::i32, Expand); @@ -3952,7 +3955,7 @@ } SDValue -ARMTargetLowering::LowerXALUO(SDValue Op, SelectionDAG &DAG) const { +ARMTargetLowering::LowerSignedALUO(SDValue Op, SelectionDAG &DAG) const { // Let legalize expand this if it isn't a legal type yet. if (!DAG.getTargetLoweringInfo().isTypeLegal(Op.getValueType())) return SDValue(); @@ -3974,6 +3977,49 @@ return DAG.getNode(ISD::MERGE_VALUES, dl, VTs, Value, Overflow); } +SDValue +ARMTargetLowering::LowerUnsignedALUO(SDValue Op, SelectionDAG &DAG) const { + // Let legalize expand this if it isn't a legal type yet. + if (!DAG.getTargetLoweringInfo().isTypeLegal(Op.getValueType())) + return SDValue(); + + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDLoc dl(Op); + + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::i32); + SDValue Value; + SDValue Overflow; + switch (Op.getOpcode()) + { + default: + llvm_unreachable("Unknown overflow instruction!"); + case ISD::UADDO: + Value = DAG.getNode(ARMISD::ADDC, dl, VTs, LHS, RHS); + // Convert the carry flag into a boolean value doing + // (ARMISD::ADDE 0, 0, Carry) + Overflow = DAG.getNode(ARMISD::ADDE, dl, VTs, + DAG.getConstant(0, dl, MVT::i32), + DAG.getConstant(0, dl, MVT::i32), + Value.getValue(1)); + break; + case ISD::USUBO: + { + Value = DAG.getNode(ARMISD::SUBC, dl, VTs, LHS, RHS); + SDValue Carry = Value.getValue(1); + // ARMISD::SUBC returns 0 when we have to borrow, so make it an overflow value. + // We do this doing (ARMISD::SUBE 1, 0, Carry) + Overflow = DAG.getNode(ARMISD::SUBE, dl, VTs, + DAG.getConstant(1, dl, MVT::i32), + DAG.getConstant(0, dl, MVT::i32), + Carry); + } + break; + } + + return DAG.getNode(ISD::MERGE_VALUES, dl, VTs, Value, Overflow); +} + SDValue ARMTargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const { SDValue Cond = Op.getOperand(0); SDValue SelectTrue = Op.getOperand(1); @@ -7380,6 +7426,49 @@ Op.getOperand(1), Op.getOperand(2)); } +static SDValue LowerADDSUBCARRY(SDValue Op, SelectionDAG &DAG) { + SDNode *N = Op.getNode(); + EVT VT = N->getValueType(0); + SDVTList VTs = DAG.getVTList(VT, MVT::i32); + + SDValue Carry = Op.getOperand(2); + EVT CarryVT = Carry.getValueType(); + + SDLoc DL(Op); + + // This converts the boolean value carry into the carry flag by doing + // ARMISD::ADDC Carry, ~0 + APInt NegOne = APInt::getAllOnesValue(CarryVT.getScalarSizeInBits()); + Carry = DAG.getNode(ARMISD::ADDC, DL, DAG.getVTList(CarryVT, MVT::i32), Carry, + DAG.getConstant(NegOne, DL, CarryVT)); + + SDValue Result; + if (Op.getOpcode() == ISD::ADDCARRY) { + // Do the addition proper using the carry flag we wanted. + Result = DAG.getNode(ARMISD::ADDE, DL, VTs, Op.getOperand(0), + Op.getOperand(1), Carry.getValue(1)); + + // Now convert the carry flag into a boolean value. We do this + // using ARMISDD:ADDE 0, 0, Carry + Carry = DAG.getNode(ARMISD::ADDE, DL, VTs, DAG.getConstant(0, DL, MVT::i32), + DAG.getConstant(0, DL, MVT::i32), Result.getValue(1)); + + } else { + // Do the subtraction proper using the carry flag we wanted. + Result = DAG.getNode(ARMISD::SUBE, DL, VTs, Op.getOperand(0), + Op.getOperand(1), Carry.getValue(1)); + + // Now convert the carry flag into a boolean value. We do this using + // ARMISDD:SUBE 1, 0, Carry because SUBCARRY actually returns a borrow not a + // carry. + Carry = DAG.getNode(ARMISD::SUBE, DL, VTs, DAG.getConstant(1, DL, MVT::i32), + DAG.getConstant(0, DL, MVT::i32), Result.getValue(1)); + } + + // Return both values. + return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Result, Carry); +} + SDValue ARMTargetLowering::LowerFSINCOS(SDValue Op, SelectionDAG &DAG) const { assert(Subtarget->isTargetDarwin()); @@ -7734,11 +7823,14 @@ case ISD::ADDE: case ISD::SUBC: case ISD::SUBE: return LowerADDC_ADDE_SUBC_SUBE(Op, DAG); + case ISD::ADDCARRY: + case ISD::SUBCARRY: return LowerADDSUBCARRY(Op, DAG); case ISD::SADDO: - case ISD::UADDO: case ISD::SSUBO: + return LowerSignedALUO(Op, DAG); + case ISD::UADDO: case ISD::USUBO: - return LowerXALUO(Op, DAG); + return LowerUnsignedALUO(Op, DAG); case ISD::ATOMIC_LOAD: case ISD::ATOMIC_STORE: return LowerAtomicLoadStore(Op, DAG); case ISD::FSINCOS: return LowerFSINCOS(Op, DAG); @@ -9877,6 +9969,36 @@ static SDValue PerformAddcSubcCombine(SDNode *N, SelectionDAG &DAG, const ARMSubtarget *Subtarget) { + if (N->getOpcode() == ARMISD::SUBC) { + // (SUBC (ADDE 0, 0, C), 1) -> C + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + if (LHS->getOpcode() == ARMISD::ADDE && + isNullConstant(LHS->getOperand(0)) && + isNullConstant(LHS->getOperand(1)) && isOneConstant(RHS)) { + SDLoc DL(N); + return LHS->getOperand(2); + } + } + + if (N->getOpcode() == ARMISD::ADDC) { + // (ADDC (ADDE 0, 0, C), -1) -> C + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + if (LHS->getOpcode() == ARMISD::ADDE && + isNullConstant(LHS->getOperand(0)) && + isNullConstant(LHS->getOperand(1)) && isAllOnesConstant(RHS)) { + SDLoc DL(N); + return LHS->getOperand(2); + } + // (ADDC (SUBE 1, 0, C), -1) -> C + if (LHS->getOpcode() == ARMISD::SUBE && isOneConstant(LHS->getOperand(0)) && + isNullConstant(LHS->getOperand(1)) && isAllOnesConstant(RHS)) { + SDLoc DL(N); + return LHS->getOperand(2); + } + } + if (Subtarget->isThumb1Only()) { SDValue RHS = N->getOperand(1); if (ConstantSDNode *C = dyn_cast(RHS)) { @@ -9895,6 +10017,16 @@ static SDValue PerformAddeSubeCombine(SDNode *N, SelectionDAG &DAG, const ARMSubtarget *Subtarget) { + // (ADDE 0, 0, (SUBC X, 1)) -> X + if (N->getOpcode() == ARMISD::ADDE) { + SDValue Carry = N->getOperand(2); + if (isNullConstant(N->getOperand(0)) && isNullConstant(N->getOperand(1)) && + Carry->getOpcode() == ARMISD::SUBC && + isOneConstant(Carry->getOperand(1))) { + return Carry->getOperand(0); + } + } + if (Subtarget->isThumb1Only()) { SDValue RHS = N->getOperand(1); if (ConstantSDNode *C = dyn_cast(RHS)) { @@ -11765,6 +11897,14 @@ return SDValue(); } +static const APInt *isPowerOf2Constant(SDValue V) { + ConstantSDNode *C = dyn_cast(V); + if (!C) + return nullptr; + const APInt *CV = &C->getAPIntValue(); + return CV->isPowerOf2() ? CV : nullptr; +} + SDValue ARMTargetLowering::PerformCMOVToBFICombine(SDNode *CMOV, SelectionDAG &DAG) const { // If we have a CMOV, OR and AND combination such as: // if (x & CN) @@ -11793,8 +11933,8 @@ SDValue And = CmpZ->getOperand(0); if (And->getOpcode() != ISD::AND) return SDValue(); - ConstantSDNode *AndC = dyn_cast(And->getOperand(1)); - if (!AndC || !AndC->getAPIntValue().isPowerOf2()) + const APInt *AndC = isPowerOf2Constant(And->getOperand(1)); + if (!AndC) return SDValue(); SDValue X = And->getOperand(0); @@ -11834,7 +11974,7 @@ SDValue V = Y; SDLoc dl(X); EVT VT = X.getValueType(); - unsigned BitInX = AndC->getAPIntValue().logBase2(); + unsigned BitInX = AndC->logBase2(); if (BitInX != 0) { // We must shift X first. @@ -12669,9 +12809,22 @@ case ARMISD::ADDE: case ARMISD::SUBC: case ARMISD::SUBE: + // Special cases when we convert a carry to a boolean. + if (Op.getResNo() == 0) { + // (ADDE 0, 0, C) will give us a single bit. + if (Op->getOpcode() == ARMISD::ADDE && isNullConstant(Op.getOperand(0)) && + isNullConstant(Op.getOperand(1))) { + Known.Zero |= APInt::getHighBitsSet(BitWidth, BitWidth - 1); + break; + } + // (SUBE 1, 0, C) will give us a single bit. + if (Op->getOpcode() == ARMISD::SUBE && isOneConstant(Op.getOperand(0)) && + isNullConstant(Op.getOperand(1))) { + Known.Zero |= APInt::getHighBitsSet(BitWidth, BitWidth - 1); + break; + } + } // These nodes' second result is a boolean - if (Op.getResNo() == 0) - break; Known.Zero |= APInt::getHighBitsSet(BitWidth, BitWidth - 1); break; case ARMISD::CMOV: { Index: test/CodeGen/ARM/intrinsics-overflow.ll =================================================================== --- test/CodeGen/ARM/intrinsics-overflow.ll +++ test/CodeGen/ARM/intrinsics-overflow.ll @@ -7,10 +7,9 @@ ret i32 %2 ; CHECK-LABEL: uadd_overflow: - ; CHECK: add r[[R2:[0-9]+]], r[[R0:[0-9]+]], r[[R1:[0-9]+]] - ; CHECK: mov r[[R1]], #1 - ; CHECK: cmp r[[R2]], r[[R0]] - ; CHECK: movhs r[[R1]], #0 + ; CHEC: adds r[[R0:[0-9]+]], r[[R0]], r[[R1:[0-9]+]] + ; CHEC: mov r[[R2:[0-9]+]], #0 + ; CHEC: adc r[[R0]], r[[R2]], #0 } @@ -34,9 +33,9 @@ ret i32 %2 ; CHECK-LABEL: usub_overflow: - ; CHECK: mov r[[R2]], #1 - ; CHECK: cmp r[[R0]], r[[R1]] - ; CHECK: movhs r[[R2]], #0 + ; CHECK: subs r[[R0:[0-9]+]], r[[R0]], r[[R1:[0-9]+]] + ; CHECK: mov r[[R2:[0-9]+]], #1 + ; CHECK: sbc r[[R0]], r[[R2]], #0 } define i32 @ssub_overflow(i32 %a, i32 %b) #0 {