Index: lib/Target/RISCV/RISCVISelLowering.h =================================================================== --- lib/Target/RISCV/RISCVISelLowering.h +++ lib/Target/RISCV/RISCVISelLowering.h @@ -101,6 +101,8 @@ SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerUADDSUBO(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSADDSUBO(SDValue Op, SelectionDAG &DAG) const; bool IsEligibleForTailCallOptimization(CCState &CCInfo, CallLoweringInfo &CLI, MachineFunction &MF, Index: lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVISelLowering.cpp +++ lib/Target/RISCV/RISCVISelLowering.cpp @@ -80,6 +80,12 @@ for (auto VT : {MVT::i1, MVT::i8, MVT::i16}) setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand); + setOperationAction(ISD::UADDO, XLenVT, Custom); + setOperationAction(ISD::USUBO, XLenVT, Custom); + + setOperationAction(ISD::SADDO, XLenVT, Custom); + setOperationAction(ISD::SSUBO, XLenVT, Custom); + if (!Subtarget.hasStdExtM()) { setOperationAction(ISD::MUL, XLenVT, Expand); setOperationAction(ISD::MULHS, XLenVT, Expand); @@ -281,6 +287,12 @@ return LowerFRAMEADDR(Op, DAG); case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG); + case ISD::UADDO: + case ISD::USUBO: + return LowerUADDSUBO(Op, DAG); + case ISD::SADDO: + case ISD::SSUBO: + return LowerSADDSUBO(Op, DAG); } } @@ -480,6 +492,77 @@ return DAG.getCopyFromReg(DAG.getEntryNode(), DL, Reg, XLenVT); } +SDValue RISCVTargetLowering::LowerUADDSUBO(SDValue Op, + SelectionDAG &DAG) const { + MVT XLenVT = Subtarget.getXLenVT(); + + SDNode *N = Op.getNode(); + + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + + SDLoc DL(Op); + + SDValue Result; + SDValue Overflow; + + if (Op.getOpcode() == ISD::UADDO) { + Result = DAG.getNode(ISD::ADD, DL, XLenVT, LHS, RHS); + // There is overflow if (X + Y) < X (or Y, but no need to check both) + Overflow = DAG.getSetCC(DL, XLenVT, Result, LHS, ISD::SETULT); + } else if (Op.getOpcode() == ISD::USUBO) { + // There is overflow in X - Y if X < Y + Result = DAG.getNode(ISD::SUB, DL, XLenVT, LHS, RHS); + Overflow = DAG.getSetCC(DL, XLenVT, LHS, RHS, ISD::SETULT); + } else + llvm_unreachable("Invalid opcode"); + + return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Result, Overflow); +} + +SDValue RISCVTargetLowering::LowerSADDSUBO(SDValue Op, + SelectionDAG &DAG) const { + MVT XLenVT = Subtarget.getXLenVT(); + + SDNode *N = Op.getNode(); + + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + + SDLoc DL(Op); + + SDValue Result; + SDValue Overflow; + + if (Op.getOpcode() == ISD::SADDO) { + Result = DAG.getNode(ISD::ADD, DL, XLenVT, LHS, RHS); + SDValue NegativeRHS = DAG.getSetCC( + DL, XLenVT, RHS, DAG.getConstant(0, DL, XLenVT), ISD::SETLT); // RHS < 0 + SDValue ResultLowerThanLHS = + DAG.getSetCC(DL, XLenVT, Result, LHS, ISD::SETLT); // LHS + RHS < LHS + // The add should be less than one of the operands (LHS) if and only if the + // other operand (RHS) is negative, otherwise there will be overflow. + Overflow = + DAG.getNode(ISD::XOR, DL, XLenVT, NegativeRHS, ResultLowerThanLHS); + } else if (Op.getOpcode() == ISD::SSUBO) { + Result = DAG.getNode(ISD::SUB, DL, XLenVT, LHS, RHS); + SDValue PositiveRHS = DAG.getNode( + ISD::XOR, DL, XLenVT, + DAG.getSetCC(DL, XLenVT, RHS, DAG.getConstant(0, DL, XLenVT), + ISD::SETLT), // 0 < RHS + DAG.getConstant(1, DL, XLenVT)); // 0 >= RHS + // The sub should be less than one of the operands (LHS) if and only if the + // other operand (RHS) is positive, otherwise there will be overflow. + SDValue ResultLowerThanLHS = + DAG.getSetCC(DL, XLenVT, Result, LHS, ISD::SETLT); // LHS - RHS < LHS + Overflow = + DAG.getNode(ISD::XOR, DL, XLenVT, PositiveRHS, ResultLowerThanLHS); + } else + llvm_unreachable("Invalid opcode"); + + return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Result, Overflow); +} + static MachineBasicBlock *emitSplitF64Pseudo(MachineInstr &MI, MachineBasicBlock *BB) { assert(MI.getOpcode() == RISCV::SplitF64Pseudo && "Unexpected instruction"); Index: test/CodeGen/RISCV/overflow.ll =================================================================== --- test/CodeGen/RISCV/overflow.ll +++ test/CodeGen/RISCV/overflow.ll @@ -12,15 +12,9 @@ ; RV32I: # %bb.0: # %entry ; RV32I-NEXT: add a3, a0, a1 ; RV32I-NEXT: sw a3, 0(a2) -; RV32I-NEXT: addi a2, zero, -1 -; RV32I-NEXT: slt a1, a2, a1 -; RV32I-NEXT: slt a0, a2, a0 -; RV32I-NEXT: slt a2, a2, a3 -; RV32I-NEXT: xor a2, a0, a2 -; RV32I-NEXT: xor a0, a0, a1 -; RV32I-NEXT: seqz a0, a0 -; RV32I-NEXT: snez a1, a2 -; RV32I-NEXT: and a0, a0, a1 +; RV32I-NEXT: slt a0, a3, a0 +; RV32I-NEXT: slti a1, a1, 0 +; RV32I-NEXT: xor a0, a1, a0 ; RV32I-NEXT: ret entry: %x = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b) @@ -35,15 +29,10 @@ ; RV32I: # %bb.0: # %entry ; RV32I-NEXT: sub a3, a0, a1 ; RV32I-NEXT: sw a3, 0(a2) -; RV32I-NEXT: addi a2, zero, -1 -; RV32I-NEXT: slt a1, a2, a1 -; RV32I-NEXT: slt a0, a2, a0 -; RV32I-NEXT: slt a2, a2, a3 -; RV32I-NEXT: xor a2, a0, a2 -; RV32I-NEXT: xor a0, a0, a1 -; RV32I-NEXT: snez a0, a0 -; RV32I-NEXT: snez a1, a2 -; RV32I-NEXT: and a0, a0, a1 +; RV32I-NEXT: slt a0, a3, a0 +; RV32I-NEXT: slti a1, a1, 0 +; RV32I-NEXT: xor a0, a1, a0 +; RV32I-NEXT: xori a0, a0, 1 ; RV32I-NEXT: ret entry: %x = call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b) @@ -71,8 +60,8 @@ define i1 @usub(i32 %a, i32 %b, i32* %c) { ; RV32I-LABEL: usub: ; RV32I: # %bb.0: # %entry -; RV32I-NEXT: sub a1, a0, a1 -; RV32I-NEXT: sw a1, 0(a2) +; RV32I-NEXT: sub a3, a0, a1 +; RV32I-NEXT: sw a3, 0(a2) ; RV32I-NEXT: sltu a0, a0, a1 ; RV32I-NEXT: ret entry: