diff options
| author | Louis Gerbarg <lgg@apple.com> | 2014-05-09 17:02:49 +0000 | 
|---|---|---|
| committer | Louis Gerbarg <lgg@apple.com> | 2014-05-09 17:02:49 +0000 | 
| commit | 3342bf1451f4d0844ca9355abc976cdadecbfac1 (patch) | |
| tree | 8f9f6d7e64673e43dd72776de79729869c7449c5 /llvm/lib | |
| parent | 1f54b821643b8a246a7a6161847caa177b81ee64 (diff) | |
| download | bcm5719-llvm-3342bf1451f4d0844ca9355abc976cdadecbfac1.tar.gz bcm5719-llvm-3342bf1451f4d0844ca9355abc976cdadecbfac1.zip | |
Add custom lowering for add/sub with overflow intrinsics to ARM
This patch adds support to ARM for custom lowering of the
llvm.{u|s}add.with.overflow.i32 intrinsics for i32/i64. This is particularly useful
for handling idiomatic saturating math functions as generated by
InstCombineCompare.
Test cases included.
rdar://14853450
llvm-svn: 208435
Diffstat (limited to 'llvm/lib')
| -rw-r--r-- | llvm/lib/Target/ARM/ARMISelLowering.cpp | 95 | ||||
| -rw-r--r-- | llvm/lib/Target/ARM/ARMISelLowering.h | 2 | 
2 files changed, 97 insertions, 0 deletions
| diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index e4832783fdf..702e549e89a 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -639,6 +639,11 @@ ARMTargetLowering::ARMTargetLowering(TargetMachine &TM)      }    } +  setOperationAction(ISD::SADDO, MVT::i32, Custom); +  setOperationAction(ISD::UADDO, MVT::i32, Custom); +  setOperationAction(ISD::SSUBO, MVT::i32, Custom); +  setOperationAction(ISD::USUBO, MVT::i32, Custom); +    // i64 operation support.    setOperationAction(ISD::MUL,     MVT::i64, Expand);    setOperationAction(ISD::MULHU,   MVT::i32, Expand); @@ -3222,11 +3227,96 @@ ARMTargetLowering::duplicateCmp(SDValue Cmp, SelectionDAG &DAG) const {    return DAG.getNode(ARMISD::FMSTAT, DL, MVT::Glue, Cmp);  } +std::pair<SDValue, SDValue> +ARMTargetLowering::getARMXALUOOp(SDValue Op, SelectionDAG &DAG, +                                 SDValue &ARMcc) const { +  assert(Op.getValueType() == MVT::i32 &&  "Unsupported value type"); + +  SDValue Value, OverflowCmp; +  SDValue LHS = Op.getOperand(0); +  SDValue RHS = Op.getOperand(1); + + +  // FIXME: We are currently always generating CMPs because we don't support +  // generating CMN through the backend. This is not as good as the natural +  // CMP case because it causes a register dependency and cannot be folded +  // later. + +  switch (Op.getOpcode()) { +  default: +    llvm_unreachable("Unknown overflow instruction!"); +  case ISD::SADDO: +    ARMcc = DAG.getConstant(ARMCC::VC, MVT::i32); +    Value = DAG.getNode(ISD::ADD, SDLoc(Op), Op.getValueType(), LHS, RHS); +    OverflowCmp = DAG.getNode(ARMISD::CMP, SDLoc(Op), MVT::Glue, Value, LHS); +    break; +  case ISD::UADDO: +    ARMcc = DAG.getConstant(ARMCC::HS, MVT::i32); +    Value = DAG.getNode(ISD::ADD, SDLoc(Op), Op.getValueType(), LHS, RHS); +    OverflowCmp = DAG.getNode(ARMISD::CMP, SDLoc(Op), MVT::Glue, Value, LHS); +    break; +  case ISD::SSUBO: +    ARMcc = DAG.getConstant(ARMCC::VC, MVT::i32); +    Value = DAG.getNode(ISD::SUB, SDLoc(Op), Op.getValueType(), LHS, RHS); +    OverflowCmp = DAG.getNode(ARMISD::CMP, SDLoc(Op), MVT::Glue, LHS, RHS); +    break; +  case ISD::USUBO: +    ARMcc = DAG.getConstant(ARMCC::HS, MVT::i32); +    Value = DAG.getNode(ISD::SUB, SDLoc(Op), Op.getValueType(), LHS, RHS); +    OverflowCmp = DAG.getNode(ARMISD::CMP, SDLoc(Op), MVT::Glue, LHS, RHS); +    break; +  } // switch (...) + +  return std::make_pair(Value, OverflowCmp); +} + + +SDValue +ARMTargetLowering::LowerXALUO(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 Value, OverflowCmp; +  SDValue ARMcc; +  std::tie(Value, OverflowCmp) = getARMXALUOOp(Op, DAG, ARMcc); +  SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32); +  // We use 0 and 1 as false and true values. +  SDValue TVal = DAG.getConstant(1, MVT::i32); +  SDValue FVal = DAG.getConstant(0, MVT::i32); +  EVT VT = Op.getValueType(); + +  SDValue Overflow = DAG.getNode(ARMISD::CMOV, SDLoc(Op), VT, TVal, FVal, +                                 ARMcc, CCR, OverflowCmp); + +  SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::i32); +  return DAG.getNode(ISD::MERGE_VALUES, SDLoc(Op), VTs, Value, Overflow); +} + +  SDValue ARMTargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const {    SDValue Cond = Op.getOperand(0);    SDValue SelectTrue = Op.getOperand(1);    SDValue SelectFalse = Op.getOperand(2);    SDLoc dl(Op); +  unsigned Opc = Cond.getOpcode(); + +  if (Cond.getResNo() == 1 && +      (Opc == ISD::SADDO || Opc == ISD::UADDO || Opc == ISD::SSUBO || +       Opc == ISD::USUBO)) { +    if (!DAG.getTargetLoweringInfo().isTypeLegal(Cond->getValueType(0))) +      return SDValue(); + +    SDValue Value, OverflowCmp; +    SDValue ARMcc; +    std::tie(Value, OverflowCmp) = getARMXALUOOp(Cond, DAG, ARMcc); +    SDValue CCR = DAG.getRegister(ARM::CPSR, MVT::i32); +    EVT VT = Op.getValueType(); + +    return DAG.getNode(ARMISD::CMOV, SDLoc(Op), VT, SelectTrue, SelectFalse, +                       ARMcc, CCR, OverflowCmp); + +  }    // Convert:    // @@ -6139,6 +6229,11 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {    case ISD::ADDE:    case ISD::SUBC:    case ISD::SUBE:          return LowerADDC_ADDE_SUBC_SUBE(Op, DAG); +  case ISD::SADDO: +  case ISD::UADDO: +  case ISD::SSUBO: +  case ISD::USUBO: +    return LowerXALUO(Op, DAG);    case ISD::ATOMIC_LOAD:    case ISD::ATOMIC_STORE:  return LowerAtomicLoadStore(Op, DAG);    case ISD::FSINCOS:       return LowerFSINCOS(Op, DAG); diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h index 305d23cf58a..c75433ac126 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.h +++ b/llvm/lib/Target/ARM/ARMISelLowering.h @@ -416,6 +416,7 @@ namespace llvm {      void addTypeForNEON(MVT VT, MVT PromotedLdStVT, MVT PromotedBitwiseVT);      void addDRTypeForNEON(MVT VT);      void addQRTypeForNEON(MVT VT); +    std::pair<SDValue, SDValue> getARMXALUOOp(SDValue Op, SelectionDAG &DAG, SDValue &ARMcc) const;      typedef SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPassVector;      void PassF64ArgInRegs(SDLoc dl, SelectionDAG &DAG, @@ -453,6 +454,7 @@ namespace llvm {                                   TLSModel::Model model) 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 LowerSELECT(SDValue Op, SelectionDAG &DAG) const;      SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;      SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; | 

