diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -3269,6 +3269,45 @@ case ISD::UMULFIX: Results.push_back(TLI.expandFixedPointMul(Node, DAG)); break; + case ISD::ADDCARRY: + case ISD::SUBCARRY: { + SDValue LHS = Node->getOperand(0); + SDValue RHS = Node->getOperand(1); + SDValue Carry = Node->getOperand(2); + + bool IsAdd = Node->getOpcode() == ISD::ADDCARRY; + + // Initial add of the 2 operands. + unsigned Op = IsAdd ? ISD::ADD : ISD::SUB; + EVT VT = LHS.getValueType(); + SDValue Sum = DAG.getNode(Op, dl, VT, LHS, RHS); + + // Initial check for overflow. + EVT CarryType = Node->getValueType(1); + EVT SetCCType = getSetCCResultType(Node->getValueType(0)); + ISD::CondCode CC = IsAdd ? ISD::SETULT : ISD::SETUGT; + SDValue Overflow = DAG.getSetCC(dl, SetCCType, Sum, LHS, CC); + + // Add of the sum and the carry. + SDValue Sum2 = DAG.getNode(Op, dl, VT, Sum, Carry); + + // Second check for overflow. If we are adding, we can only overflow if the + // initial sum is all 1s ang the carry is set, resulting in a new sum of 0. + // If we are subtracting, we can only overflow if the initial sum is 0 and + // the carry is set, resulting in a new sum of all 1s. + SDValue Zero = DAG.getConstant(0, dl, VT); + SDValue Overflow2 = IsAdd ? DAG.getSetCC(dl, VT, Sum2, Zero, ISD::SETEQ) + : DAG.getSetCC(dl, VT, Sum, Zero, ISD::SETEQ); + Overflow2 = DAG.getNode(ISD::AND, dl, VT, Overflow2, Carry); + + SDValue ResultCarry = + DAG.getNode(ISD::OR, dl, SetCCType, Overflow, Overflow2); + + Results.push_back(Sum2); + Results.push_back( + DAG.getBoolExtOrTrunc(ResultCarry, dl, CarryType, CarryType)); + break; + } case ISD::SADDO: case ISD::SSUBO: { SDValue LHS = Node->getOperand(0); diff --git a/llvm/test/CodeGen/SPARC/addcarry.ll b/llvm/test/CodeGen/SPARC/addcarry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPARC/addcarry.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=sparc | FileCheck %s --check-prefix=SPARC + +; Test ADDCARRY node expansion on a target that does not currently support ADDCARRY. +; Signed fixed point multiplication eventually expands down to an ADDCARRY. + +declare i64 @llvm.smul.fix.i64 (i64, i64, i32) + +define i64 @func2(i64 %x, i64 %y) { +; SPARC-LABEL: func2: +; SPARC: .cfi_startproc +; SPARC-NEXT: ! %bb.0: +; SPARC-NEXT: umul %o1, %o3, %o4 +; SPARC-NEXT: rd %y, %o5 +; SPARC-NEXT: umul %o1, %o2, %g2 +; SPARC-NEXT: rd %y, %g3 +; SPARC-NEXT: addcc %o5, %g2, %g4 +; SPARC-NEXT: addxcc %g3, 0, %g2 +; SPARC-NEXT: umul %o0, %o3, %o5 +; SPARC-NEXT: rd %y, %g3 +; SPARC-NEXT: add %g4, %o5, %o5 +; SPARC-NEXT: cmp %o5, %g4 +; SPARC-NEXT: bcs .LBB0_1 +; SPARC-NEXT: nop +; SPARC-NEXT: ! %bb.2: +; SPARC-NEXT: ba .LBB0_3 +; SPARC-NEXT: mov %g0, %g4 +; SPARC-NEXT: .LBB0_1: +; SPARC-NEXT: mov 1, %g4 +; SPARC-NEXT: .LBB0_3: +; SPARC-NEXT: add %g2, %g3, %g2 +; SPARC-NEXT: add %g2, %g4, %g2 +; SPARC-NEXT: smul %o0, %o2, %g3 +; SPARC-NEXT: cmp %o0, 0 +; SPARC-NEXT: bge .LBB0_5 +; SPARC-NEXT: add %g2, %g3, %o0 +; SPARC-NEXT: ! %bb.4: +; SPARC-NEXT: sub %o0, %o3, %o0 +; SPARC-NEXT: .LBB0_5: +; SPARC-NEXT: cmp %o2, 0 +; SPARC-NEXT: bge .LBB0_7 +; SPARC-NEXT: nop +; SPARC-NEXT: ! %bb.6: +; SPARC-NEXT: sub %o0, %o1, %o0 +; SPARC-NEXT: .LBB0_7: +; SPARC-NEXT: sll %o0, 30, %o0 +; SPARC-NEXT: srl %o5, 2, %o1 +; SPARC-NEXT: or %o1, %o0, %o0 +; SPARC-NEXT: sll %o5, 30, %o1 +; SPARC-NEXT: srl %o4, 2, %o2 +; SPARC-NEXT: retl +; SPARC-NEXT: or %o2, %o1, %o1 + %tmp = call i64 @llvm.smul.fix.i64(i64 %x, i64 %y, i32 2); + ret i64 %tmp; +}