mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
[SelectionDAG] Expand UADDO/USUBO into ADD/SUBCARRY if legal for target
Additionally, implement handling of ADD/SUBCARRY on Hexagon, utilizing the UADDO/USUBO expansion. Differential Revision: https://reviews.llvm.org/D47559 llvm-svn: 333751
This commit is contained in:
parent
96090bce8b
commit
0a4ed009a4
@ -2283,8 +2283,11 @@ SDValue DAGCombiner::visitADDCARRY(SDNode *N) {
|
|||||||
return DAG.getNode(ISD::ADDCARRY, DL, N->getVTList(), N1, N0, CarryIn);
|
return DAG.getNode(ISD::ADDCARRY, DL, N->getVTList(), N1, N0, CarryIn);
|
||||||
|
|
||||||
// fold (addcarry x, y, false) -> (uaddo x, y)
|
// fold (addcarry x, y, false) -> (uaddo x, y)
|
||||||
if (isNullConstant(CarryIn))
|
if (isNullConstant(CarryIn)) {
|
||||||
return DAG.getNode(ISD::UADDO, DL, N->getVTList(), N0, N1);
|
if (!LegalOperations ||
|
||||||
|
TLI.isOperationLegalOrCustom(ISD::UADDO, N->getValueType(0)))
|
||||||
|
return DAG.getNode(ISD::UADDO, DL, N->getVTList(), N0, N1);
|
||||||
|
}
|
||||||
|
|
||||||
// fold (addcarry 0, 0, X) -> (and (ext/trunc X), 1) and no carry.
|
// fold (addcarry 0, 0, X) -> (and (ext/trunc X), 1) and no carry.
|
||||||
if (isNullConstant(N0) && isNullConstant(N1)) {
|
if (isNullConstant(N0) && isNullConstant(N1)) {
|
||||||
@ -2592,8 +2595,11 @@ SDValue DAGCombiner::visitSUBCARRY(SDNode *N) {
|
|||||||
SDValue CarryIn = N->getOperand(2);
|
SDValue CarryIn = N->getOperand(2);
|
||||||
|
|
||||||
// fold (subcarry x, y, false) -> (usubo x, y)
|
// fold (subcarry x, y, false) -> (usubo x, y)
|
||||||
if (isNullConstant(CarryIn))
|
if (isNullConstant(CarryIn)) {
|
||||||
return DAG.getNode(ISD::USUBO, SDLoc(N), N->getVTList(), N0, N1);
|
if (!LegalOperations ||
|
||||||
|
TLI.isOperationLegalOrCustom(ISD::USUBO, N->getValueType(0)))
|
||||||
|
return DAG.getNode(ISD::USUBO, SDLoc(N), N->getVTList(), N0, N1);
|
||||||
|
}
|
||||||
|
|
||||||
return SDValue();
|
return SDValue();
|
||||||
}
|
}
|
||||||
|
@ -3499,15 +3499,25 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
|
|||||||
case ISD::USUBO: {
|
case ISD::USUBO: {
|
||||||
SDValue LHS = Node->getOperand(0);
|
SDValue LHS = Node->getOperand(0);
|
||||||
SDValue RHS = Node->getOperand(1);
|
SDValue RHS = Node->getOperand(1);
|
||||||
SDValue Sum = DAG.getNode(Node->getOpcode() == ISD::UADDO ?
|
bool IsAdd = Node->getOpcode() == ISD::UADDO;
|
||||||
ISD::ADD : ISD::SUB, dl, LHS.getValueType(),
|
// If ADD/SUBCARRY is legal, use that instead.
|
||||||
LHS, RHS);
|
unsigned OpcCarry = IsAdd ? ISD::ADDCARRY : ISD::SUBCARRY;
|
||||||
|
if (TLI.isOperationLegalOrCustom(OpcCarry, Node->getValueType(0))) {
|
||||||
|
SDValue CarryIn = DAG.getConstant(0, dl, Node->getValueType(1));
|
||||||
|
SDValue NodeCarry = DAG.getNode(OpcCarry, dl, Node->getVTList(),
|
||||||
|
{ LHS, RHS, CarryIn });
|
||||||
|
Results.push_back(SDValue(NodeCarry.getNode(), 0));
|
||||||
|
Results.push_back(SDValue(NodeCarry.getNode(), 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDValue Sum = DAG.getNode(IsAdd ? ISD::ADD : ISD::SUB, dl,
|
||||||
|
LHS.getValueType(), LHS, RHS);
|
||||||
Results.push_back(Sum);
|
Results.push_back(Sum);
|
||||||
|
|
||||||
EVT ResultType = Node->getValueType(1);
|
EVT ResultType = Node->getValueType(1);
|
||||||
EVT SetCCType = getSetCCResultType(Node->getValueType(0));
|
EVT SetCCType = getSetCCResultType(Node->getValueType(0));
|
||||||
ISD::CondCode CC
|
ISD::CondCode CC = IsAdd ? ISD::SETULT : ISD::SETUGT;
|
||||||
= Node->getOpcode() == ISD::UADDO ? ISD::SETULT : ISD::SETUGT;
|
|
||||||
SDValue SetCC = DAG.getSetCC(dl, SetCCType, Sum, LHS, CC);
|
SDValue SetCC = DAG.getSetCC(dl, SetCCType, Sum, LHS, CC);
|
||||||
|
|
||||||
Results.push_back(DAG.getBoolExtOrTrunc(SetCC, dl, ResultType, ResultType));
|
Results.push_back(DAG.getBoolExtOrTrunc(SetCC, dl, ResultType, ResultType));
|
||||||
|
@ -762,6 +762,15 @@ void HexagonDAGToDAGISel::SelectFrameIndex(SDNode *N) {
|
|||||||
ReplaceNode(N, R);
|
ReplaceNode(N, R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HexagonDAGToDAGISel::SelectAddSubCarry(SDNode *N) {
|
||||||
|
unsigned OpcCarry = N->getOpcode() == HexagonISD::ADDC ? Hexagon::A4_addp_c
|
||||||
|
: Hexagon::A4_subp_c;
|
||||||
|
SDNode *C = CurDAG->getMachineNode(OpcCarry, SDLoc(N), N->getVTList(),
|
||||||
|
{ N->getOperand(0), N->getOperand(1),
|
||||||
|
N->getOperand(2) });
|
||||||
|
ReplaceNode(N, C);
|
||||||
|
}
|
||||||
|
|
||||||
void HexagonDAGToDAGISel::SelectVAlign(SDNode *N) {
|
void HexagonDAGToDAGISel::SelectVAlign(SDNode *N) {
|
||||||
MVT ResTy = N->getValueType(0).getSimpleVT();
|
MVT ResTy = N->getValueType(0).getSimpleVT();
|
||||||
if (HST->isHVXVectorType(ResTy, true))
|
if (HST->isHVXVectorType(ResTy, true))
|
||||||
@ -875,6 +884,9 @@ void HexagonDAGToDAGISel::Select(SDNode *N) {
|
|||||||
case ISD::STORE: return SelectStore(N);
|
case ISD::STORE: return SelectStore(N);
|
||||||
case ISD::INTRINSIC_W_CHAIN: return SelectIntrinsicWChain(N);
|
case ISD::INTRINSIC_W_CHAIN: return SelectIntrinsicWChain(N);
|
||||||
case ISD::INTRINSIC_WO_CHAIN: return SelectIntrinsicWOChain(N);
|
case ISD::INTRINSIC_WO_CHAIN: return SelectIntrinsicWOChain(N);
|
||||||
|
|
||||||
|
case HexagonISD::ADDC:
|
||||||
|
case HexagonISD::SUBC: return SelectAddSubCarry(N);
|
||||||
case HexagonISD::VALIGN: return SelectVAlign(N);
|
case HexagonISD::VALIGN: return SelectVAlign(N);
|
||||||
case HexagonISD::VALIGNADDR: return SelectVAlignAddr(N);
|
case HexagonISD::VALIGNADDR: return SelectVAlignAddr(N);
|
||||||
case HexagonISD::TYPECAST: return SelectTypecast(N);
|
case HexagonISD::TYPECAST: return SelectTypecast(N);
|
||||||
|
@ -105,6 +105,7 @@ public:
|
|||||||
void SelectV65Gather(SDNode *N);
|
void SelectV65Gather(SDNode *N);
|
||||||
void SelectV65GatherPred(SDNode *N);
|
void SelectV65GatherPred(SDNode *N);
|
||||||
void SelectHVXDualOutput(SDNode *N);
|
void SelectHVXDualOutput(SDNode *N);
|
||||||
|
void SelectAddSubCarry(SDNode *N);
|
||||||
void SelectVAlign(SDNode *N);
|
void SelectVAlign(SDNode *N);
|
||||||
void SelectVAlignAddr(SDNode *N);
|
void SelectVAlignAddr(SDNode *N);
|
||||||
void SelectTypecast(SDNode *N);
|
void SelectTypecast(SDNode *N);
|
||||||
|
@ -1327,13 +1327,18 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
|
|||||||
setMinimumJumpTableEntries(std::numeric_limits<int>::max());
|
setMinimumJumpTableEntries(std::numeric_limits<int>::max());
|
||||||
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
|
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
|
||||||
|
|
||||||
// Only add and sub that detect overflow are the saturating ones.
|
// Hexagon has A4_addp_c and A4_subp_c that take and generate a carry bit,
|
||||||
|
// but they only operate on i64.
|
||||||
for (MVT VT : MVT::integer_valuetypes()) {
|
for (MVT VT : MVT::integer_valuetypes()) {
|
||||||
setOperationAction(ISD::UADDO, VT, Expand);
|
setOperationAction(ISD::UADDO, VT, Expand);
|
||||||
setOperationAction(ISD::SADDO, VT, Expand);
|
setOperationAction(ISD::USUBO, VT, Expand);
|
||||||
setOperationAction(ISD::USUBO, VT, Expand);
|
setOperationAction(ISD::SADDO, VT, Expand);
|
||||||
setOperationAction(ISD::SSUBO, VT, Expand);
|
setOperationAction(ISD::SSUBO, VT, Expand);
|
||||||
|
setOperationAction(ISD::ADDCARRY, VT, Expand);
|
||||||
|
setOperationAction(ISD::SUBCARRY, VT, Expand);
|
||||||
}
|
}
|
||||||
|
setOperationAction(ISD::ADDCARRY, MVT::i64, Custom);
|
||||||
|
setOperationAction(ISD::SUBCARRY, MVT::i64, Custom);
|
||||||
|
|
||||||
setOperationAction(ISD::CTLZ, MVT::i8, Promote);
|
setOperationAction(ISD::CTLZ, MVT::i8, Promote);
|
||||||
setOperationAction(ISD::CTLZ, MVT::i16, Promote);
|
setOperationAction(ISD::CTLZ, MVT::i16, Promote);
|
||||||
@ -1681,6 +1686,8 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
|
|||||||
|
|
||||||
const char* HexagonTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
const char* HexagonTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||||
switch ((HexagonISD::NodeType)Opcode) {
|
switch ((HexagonISD::NodeType)Opcode) {
|
||||||
|
case HexagonISD::ADDC: return "HexagonISD::ADDC";
|
||||||
|
case HexagonISD::SUBC: return "HexagonISD::SUBC";
|
||||||
case HexagonISD::ALLOCA: return "HexagonISD::ALLOCA";
|
case HexagonISD::ALLOCA: return "HexagonISD::ALLOCA";
|
||||||
case HexagonISD::AT_GOT: return "HexagonISD::AT_GOT";
|
case HexagonISD::AT_GOT: return "HexagonISD::AT_GOT";
|
||||||
case HexagonISD::AT_PCREL: return "HexagonISD::AT_PCREL";
|
case HexagonISD::AT_PCREL: return "HexagonISD::AT_PCREL";
|
||||||
@ -2705,6 +2712,24 @@ HexagonTargetLowering::LowerUnalignedLoad(SDValue Op, SelectionDAG &DAG)
|
|||||||
return M;
|
return M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDValue
|
||||||
|
HexagonTargetLowering::LowerAddSubCarry(SDValue Op, SelectionDAG &DAG) const {
|
||||||
|
const SDLoc &dl(Op);
|
||||||
|
unsigned Opc = Op.getOpcode();
|
||||||
|
SDValue X = Op.getOperand(0), Y = Op.getOperand(1), C = Op.getOperand(2);
|
||||||
|
|
||||||
|
if (Opc == ISD::ADDCARRY)
|
||||||
|
return DAG.getNode(HexagonISD::ADDC, dl, Op.getNode()->getVTList(),
|
||||||
|
{ X, Y, C });
|
||||||
|
|
||||||
|
EVT CarryTy = C.getValueType();
|
||||||
|
SDValue SubC = DAG.getNode(HexagonISD::SUBC, dl, Op.getNode()->getVTList(),
|
||||||
|
{ X, Y, DAG.getLogicalNOT(dl, C, CarryTy) });
|
||||||
|
SDValue Out[] = { SubC.getValue(0),
|
||||||
|
DAG.getLogicalNOT(dl, SubC.getValue(1), CarryTy) };
|
||||||
|
return DAG.getMergeValues(Out, dl);
|
||||||
|
}
|
||||||
|
|
||||||
SDValue
|
SDValue
|
||||||
HexagonTargetLowering::LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const {
|
HexagonTargetLowering::LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const {
|
||||||
SDValue Chain = Op.getOperand(0);
|
SDValue Chain = Op.getOperand(0);
|
||||||
@ -2763,6 +2788,8 @@ HexagonTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
|||||||
case ISD::VECTOR_SHUFFLE: return LowerVECTOR_SHUFFLE(Op, DAG);
|
case ISD::VECTOR_SHUFFLE: return LowerVECTOR_SHUFFLE(Op, DAG);
|
||||||
case ISD::BITCAST: return LowerBITCAST(Op, DAG);
|
case ISD::BITCAST: return LowerBITCAST(Op, DAG);
|
||||||
case ISD::LOAD: return LowerUnalignedLoad(Op, DAG);
|
case ISD::LOAD: return LowerUnalignedLoad(Op, DAG);
|
||||||
|
case ISD::ADDCARRY:
|
||||||
|
case ISD::SUBCARRY: return LowerAddSubCarry(Op, DAG);
|
||||||
case ISD::SRA:
|
case ISD::SRA:
|
||||||
case ISD::SHL:
|
case ISD::SHL:
|
||||||
case ISD::SRL: return LowerVECTOR_SHIFT(Op, DAG);
|
case ISD::SRL: return LowerVECTOR_SHIFT(Op, DAG);
|
||||||
|
@ -36,6 +36,8 @@ namespace HexagonISD {
|
|||||||
|
|
||||||
CONST32 = OP_BEGIN,
|
CONST32 = OP_BEGIN,
|
||||||
CONST32_GP, // For marking data present in GP.
|
CONST32_GP, // For marking data present in GP.
|
||||||
|
ADDC, // Add with carry: (X, Y, Cin) -> (X+Y, Cout).
|
||||||
|
SUBC, // Sub with carry: (X, Y, Cin) -> (X+~Y+Cin, Cout).
|
||||||
ALLOCA,
|
ALLOCA,
|
||||||
|
|
||||||
AT_GOT, // Index in GOT.
|
AT_GOT, // Index in GOT.
|
||||||
@ -162,6 +164,7 @@ namespace HexagonISD {
|
|||||||
SDValue LowerSIGN_EXTEND(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerSIGN_EXTEND(SDValue Op, SelectionDAG &DAG) const;
|
||||||
SDValue LowerZERO_EXTEND(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerZERO_EXTEND(SDValue Op, SelectionDAG &DAG) const;
|
||||||
SDValue LowerUnalignedLoad(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerUnalignedLoad(SDValue Op, SelectionDAG &DAG) const;
|
||||||
|
SDValue LowerAddSubCarry(SDValue Op, SelectionDAG &DAG) const;
|
||||||
|
|
||||||
SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
|
||||||
SDValue LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const;
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
; RUN: llc -march=hexagon -hexagon-expand-condsets=0 < %s | FileCheck %s
|
|
||||||
|
|
||||||
; CHECK-DAG: r{{[0-9]+:[0-9]+}} = add(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+:[0-9]+}} = add(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: p{{[0-9]+}} = cmp.gtu(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: p{{[0-9]+}} = cmp.gtu(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
|
|
||||||
|
|
||||||
define void @check_adde_addc(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64* %a4, i64* %a5) {
|
|
||||||
b6:
|
|
||||||
%v7 = zext i64 %a0 to i128
|
|
||||||
%v8 = zext i64 %a1 to i128
|
|
||||||
%v9 = shl i128 %v8, 64
|
|
||||||
%v10 = or i128 %v7, %v9
|
|
||||||
%v11 = zext i64 %a2 to i128
|
|
||||||
%v12 = zext i64 %a3 to i128
|
|
||||||
%v13 = shl i128 %v12, 64
|
|
||||||
%v14 = or i128 %v11, %v13
|
|
||||||
%v15 = add i128 %v10, %v14
|
|
||||||
%v16 = lshr i128 %v15, 64
|
|
||||||
%v17 = trunc i128 %v15 to i64
|
|
||||||
%v18 = trunc i128 %v16 to i64
|
|
||||||
store i64 %v17, i64* %a4
|
|
||||||
store i64 %v18, i64* %a5
|
|
||||||
ret void
|
|
||||||
}
|
|
25
test/CodeGen/Hexagon/addsubcarry.ll
Normal file
25
test/CodeGen/Hexagon/addsubcarry.ll
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
; RUN: llc -march=hexagon < %s | FileCheck %s
|
||||||
|
|
||||||
|
@g = global i128 zeroinitializer, align 8
|
||||||
|
|
||||||
|
; CHECK-LABEL: addc:
|
||||||
|
; CHECK: p[[P0:[0-3]]] = and(p[[P1:[0-9]]],!p[[P1]])
|
||||||
|
; CHECK: add({{.*}},{{.*}},p[[P0]]):carry
|
||||||
|
; CHECK: add({{.*}},{{.*}},p[[P0]]):carry
|
||||||
|
define void @addc(i128 %a0, i128 %a1) #0 {
|
||||||
|
%v0 = add i128 %a0, %a1
|
||||||
|
store i128 %v0, i128* @g, align 8
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: subc:
|
||||||
|
; CHECK: p[[P0:[0-3]]] = or(p[[P1:[0-9]]],!p[[P1]])
|
||||||
|
; CHECK: sub({{.*}},{{.*}},p[[P0]]):carry
|
||||||
|
; CHECK: sub({{.*}},{{.*}},p[[P0]]):carry
|
||||||
|
define void @subc(i128 %a0, i128 %a1) #0 {
|
||||||
|
%v0 = sub i128 %a0, %a1
|
||||||
|
store i128 %v0, i128* @g, align 8
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
; RUN: llc -march=hexagon -hexagon-expand-condsets=0 < %s | FileCheck %s
|
|
||||||
|
|
||||||
; CHECK-DAG: r{{[0-9]+:[0-9]+}} = sub(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+:[0-9]+}} = sub(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: p{{[0-9]+}} = cmp.gtu(r{{[0-9]+:[0-9]+}},r{{[0-9]+:[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
|
|
||||||
; CHECK-DAG: r{{[0-9]+}} = mux(p{{[0-9]+}},r{{[0-9]+}},r{{[0-9]+}})
|
|
||||||
|
|
||||||
define void @check_sube_subc(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64* %a4, i64* %a5) {
|
|
||||||
b6:
|
|
||||||
%v7 = zext i64 %a0 to i128
|
|
||||||
%v8 = zext i64 %a1 to i128
|
|
||||||
%v9 = shl i128 %v8, 64
|
|
||||||
%v10 = or i128 %v7, %v9
|
|
||||||
%v11 = zext i64 %a2 to i128
|
|
||||||
%v12 = zext i64 %a3 to i128
|
|
||||||
%v13 = shl i128 %v12, 64
|
|
||||||
%v14 = or i128 %v11, %v13
|
|
||||||
%v15 = sub i128 %v10, %v14
|
|
||||||
%v16 = lshr i128 %v15, 64
|
|
||||||
%v17 = trunc i128 %v15 to i64
|
|
||||||
%v18 = trunc i128 %v16 to i64
|
|
||||||
store i64 %v17, i64* %a4
|
|
||||||
store i64 %v18, i64* %a5
|
|
||||||
ret void
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user