[llvm-commits] Support for ARM Run-Time ABI

Renato Golin renato.golin at arm.com
Fri Sep 17 08:48:58 PDT 2010


Rationale: When choosing EABI variant for ARM targets, LLVM should use
functions as defined by the RTABI document [1] to be compatible with
other compilers that follow the same standard, including armcc and
CodeSourcery's GCC. This patch includes some of the specifications
that get automatically selected when the Target Triple is chosen to
conform to the EABI ("arm/thumb*-eabi-*"). Comments are provided,
indicating the specific section of the document it conforms to.

Contents: This patch contains three changes:
 1. Floating-Point helpers: LLVM selects EABI helpers instead of the
default GNU ones, also compatible with CodeSourcery's GCC.
 2. Remainder helper: LLVM lowers the remainder operation by selecting
the helper function and reading the remainder from the appropriate
register.
 3. Memset: EABI memset has a different order of parameters, so we
need to do custom lowering to move them around.

Authors: Renato Golin & Evzen Muller (ARM Ltd.)

[1] http://infocenter.arm.com/help/topic/com.arm.doc.ihi0043c/IHI0043C_rtabi.pdf

--------------------------------------------------------------------------------
PATCH

Index: lib/Target/ARM/ARMISelLowering.h
===================================================================
--- lib/Target/ARM/ARMISelLowering.h	(.../import/current)	(revision 160856)
+++ lib/Target/ARM/ARMISelLowering.h	(.../trunk)	(revision 160856)
@@ -351,6 +351,8 @@
     SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerShiftRightParts(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerShiftLeftParts(SDValue Op, SelectionDAG &DAG) const;
+    // Lowers using REM helpers, see RTABI section 4.2/4.3
+    SDValue LowerREM(SDNode *N, SelectionDAG &DAG, bool isSigned) const;
     SDValue LowerFLT_ROUNDS_(SDValue Op, SelectionDAG &DAG) const;

     SDValue LowerCallResult(SDValue Chain, SDValue InFlag,

Index: lib/Target/ARM/ARMISelLowering.cpp
===================================================================
--- lib/Target/ARM/ARMISelLowering.cpp	(.../import/current)	(revision 160856)
+++ lib/Target/ARM/ARMISelLowering.cpp	(.../trunk)	(revision 160856)
@@ -26,6 +26,7 @@
 #include "ARMTargetObjectFile.h"
 #include "llvm/CallingConv.h"
 #include "llvm/Constants.h"
+#include "llvm/DerivedTypes.h"
 #include "llvm/Function.h"
 #include "llvm/GlobalValue.h"
 #include "llvm/Instruction.h"
@@ -252,7 +253,117 @@
       setLibcallCallingConv(static_cast<RTLIB::Libcall>(i),
                             CallingConv::ARM_AAPCS);
     }
+
+    // generate calls to runtime FP helpers, see RTABI section 4.1
+    // Single-precision floating-point arithmetic.
+    setLibcallName(RTLIB::ADD_F32, "__aeabi_fadd");
+    setLibcallName(RTLIB::SUB_F32, "__aeabi_fsub");
+    setLibcallName(RTLIB::MUL_F32, "__aeabi_fmul");
+    setLibcallName(RTLIB::DIV_F32, "__aeabi_fdiv");
+
+    // Single-precision comparisons.
+    setLibcallName(RTLIB::OEQ_F32, "__aeabi_fcmpeq");
+    setLibcallName(RTLIB::UNE_F32, "__aeabi_fcmpeq");
+    setLibcallName(RTLIB::OLT_F32, "__aeabi_fcmplt");
+    setLibcallName(RTLIB::OLE_F32, "__aeabi_fcmple");
+    setLibcallName(RTLIB::OGE_F32, "__aeabi_fcmpge");
+    setLibcallName(RTLIB::OGT_F32, "__aeabi_fcmpgt");
+    setLibcallName(RTLIB::UO_F32,  "__aeabi_fcmpun");
+    setLibcallName(RTLIB::O_F32,   "__aeabi_fcmpun");
+
+    // Condition used to check result
+    setCmpLibcallCC(RTLIB::OEQ_F32, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::UNE_F32, ISD::SETEQ);
+    setCmpLibcallCC(RTLIB::OLT_F32, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OLE_F32, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OGE_F32, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OGT_F32, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::UO_F32,  ISD::SETNE);
+    setCmpLibcallCC(RTLIB::O_F32,   ISD::SETEQ);
+
+    // Double-precision floating-point arithmetic.
+    setLibcallName(RTLIB::ADD_F64, "__aeabi_dadd");
+    setLibcallName(RTLIB::SUB_F64, "__aeabi_dsub");
+    setLibcallName(RTLIB::MUL_F64, "__aeabi_dmul");
+    setLibcallName(RTLIB::DIV_F64, "__aeabi_ddiv");
+
+    // Double-precision comparisons.
+    setLibcallName(RTLIB::OEQ_F64, "__aeabi_dcmpeq");
+    setLibcallName(RTLIB::UNE_F64, "__aeabi_dcmpeq");
+    setLibcallName(RTLIB::OLT_F64, "__aeabi_dcmplt");
+    setLibcallName(RTLIB::OLE_F64, "__aeabi_dcmple");
+    setLibcallName(RTLIB::OGE_F64, "__aeabi_dcmpge");
+    setLibcallName(RTLIB::OGT_F64, "__aeabi_dcmpgt");
+    setLibcallName(RTLIB::UO_F64,  "__aeabi_dcmpun");
+    setLibcallName(RTLIB::O_F64,   "__aeabi_dcmpun");
+
+    // Condition used to check result
+    setCmpLibcallCC(RTLIB::OEQ_F64, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::UNE_F64, ISD::SETEQ);
+    setCmpLibcallCC(RTLIB::OLT_F64, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OLE_F64, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OGE_F64, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::OGT_F64, ISD::SETNE);
+    setCmpLibcallCC(RTLIB::UO_F64,  ISD::SETNE);
+    setCmpLibcallCC(RTLIB::O_F64,   ISD::SETEQ);
+
+    // Conversions between floating types.
+    setLibcallName(RTLIB::FPROUND_F64_F32, "__aeabi_d2f");
+    setLibcallName(RTLIB::FPEXT_F32_F64,   "__aeabi_f2d");
+
+    // Floating-point to integer conversions.
+    setLibcallName(RTLIB::FPTOSINT_F64_I32, "__aeabi_d2iz");
+    setLibcallName(RTLIB::FPTOUINT_F64_I32, "__aeabi_d2uiz");
+    setLibcallName(RTLIB::FPTOSINT_F32_I32, "__aeabi_f2iz");
+    setLibcallName(RTLIB::FPTOUINT_F32_I32, "__aeabi_f2uiz");
+    setLibcallName(RTLIB::FPTOSINT_F32_I64, "__aeabi_f2lz");
+    setLibcallName(RTLIB::FPTOUINT_F32_I64, "__aeabi_f2ulz");
+    setLibcallName(RTLIB::FPTOSINT_F64_I64, "__aeabi_d2lz");
+    setLibcallName(RTLIB::FPTOUINT_F64_I64, "__aeabi_d2ulz");
+
+    // Integer to floating-point conversions.
+    setLibcallName(RTLIB::SINTTOFP_I32_F64, "__aeabi_i2d");
+    setLibcallName(RTLIB::UINTTOFP_I32_F64, "__aeabi_ui2d");
+    setLibcallName(RTLIB::SINTTOFP_I32_F32, "__aeabi_i2f");
+    setLibcallName(RTLIB::UINTTOFP_I32_F32, "__aeabi_ui2f");
+    setLibcallName(RTLIB::SINTTOFP_I64_F64, "__aeabi_l2d");
+    setLibcallName(RTLIB::UINTTOFP_I64_F64, "__aeabi_ul2d");
+    setLibcallName(RTLIB::SINTTOFP_I64_F32, "__aeabi_l2f");
+    setLibcallName(RTLIB::UINTTOFP_I64_F32, "__aeabi_ul2f");
+
+    // Memory operations
+    setLibcallName(RTLIB::MEMCPY,  "__aeabi_memcpy");
+    setLibcallName(RTLIB::MEMMOVE, "__aeabi_memmove");
+    setLibcallName(RTLIB::MEMSET,  "__aeabi_memset");
+
+    // Integer division
+    setLibcallName(RTLIB::SDIV_I8,  "__aeabi_idiv");
+    setLibcallName(RTLIB::SDIV_I16, "__aeabi_idiv");
+    setLibcallName(RTLIB::SDIV_I32, "__aeabi_idiv");
+    setLibcallName(RTLIB::SDIV_I64, "__aeabi_ldivmod");
+
+    setLibcallName(RTLIB::UDIV_I8,  "__aeabi_uidiv");
+    setLibcallName(RTLIB::UDIV_I16, "__aeabi_uidiv");
+    setLibcallName(RTLIB::UDIV_I32, "__aeabi_uidiv");
+    setLibcallName(RTLIB::UDIV_I64, "__aeabi_uldivmod");
+
+    // Lowers using REM helpers, see RTABI section 4.2/4.3
+    // Use custom actions for srem/urem
+    setOperationAction(ISD::SREM, MVT::i8, Custom);
+    setOperationAction(ISD::SREM, MVT::i16, Custom);
+    setOperationAction(ISD::SREM, MVT::i32, Custom);
+    setOperationAction(ISD::SREM, MVT::i64, Custom);
+
+    setOperationAction(ISD::UREM, MVT::i8, Custom);
+    setOperationAction(ISD::UREM, MVT::i16, Custom);
+    setOperationAction(ISD::UREM, MVT::i32, Custom);
+    setOperationAction(ISD::UREM, MVT::i64, Custom);
   }
+  else {
+    // These are expanded into libcalls.
+    setOperationAction(ISD::SDIV,  MVT::i32, Expand);
+    setOperationAction(ISD::UDIV,  MVT::i32, Expand);
+  }

   if (Subtarget->isThumb1Only())
     addRegisterClass(MVT::i32, ARM::tGPRRegisterClass);
@@ -381,17 +492,6 @@
   if (!Subtarget->hasV6Ops())
     setOperationAction(ISD::BSWAP, MVT::i32, Expand);

-  // These are expanded into libcalls.
-  if (!Subtarget->hasDivide()) {
-    // v7M has a hardware divider
-    setOperationAction(ISD::SDIV,  MVT::i32, Expand);
-    setOperationAction(ISD::UDIV,  MVT::i32, Expand);
-  }
-  setOperationAction(ISD::SREM,  MVT::i32, Expand);
-  setOperationAction(ISD::UREM,  MVT::i32, Expand);
-  setOperationAction(ISD::SDIVREM, MVT::i32, Expand);
-  setOperationAction(ISD::UDIVREM, MVT::i32, Expand);
-
   setOperationAction(ISD::GlobalAddress, MVT::i32,   Custom);
   setOperationAction(ISD::ConstantPool,  MVT::i32,   Custom);
   setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom);
@@ -526,6 +626,11 @@
     }
   }

+  setOperationAction(ISD::SDIV,  MVT::i32, Expand);
+  setOperationAction(ISD::UDIV,  MVT::i32, Expand);
+  setOperationAction(ISD::SDIVREM, MVT::i32, Expand);
+  setOperationAction(ISD::UDIVREM, MVT::i32, Expand);
+
   // We have target-specific dag combine patterns for the following nodes:
   // ARMISD::VMOVRRD  - No need to call setTargetDAGCombine
   setTargetDAGCombine(ISD::ADD);
@@ -2775,6 +2880,47 @@
  return DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, Lo, Hi);
 }

+// Lowers using REM helpers, see RTABI section 4.2/4.3
+SDValue ARMTargetLowering::LowerREM(SDNode *N, SelectionDAG &DAG,
bool isSigned) const {
+  ArgListTy Args;
+  ArgListEntry Entry;
+  EVT PtrVT = getPointerTy();
+  SDValue Chain = DAG.getEntryNode();
+  // Setup arguments
+  for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i) {
+      EVT ArgVT = N->getOperand(i).getValueType();
+      const Type *ArgTy = ArgVT.getTypeForEVT(*DAG.getContext());
+      Entry.Node = N->getOperand(i);
+      Entry.Ty = ArgTy;
+      Entry.isSExt = isSigned;
+      Entry.isZExt = !isSigned;
+      Args.push_back(Entry);
+  }
+  // Build return type
+  std::vector<const Type*> RetTyParams;
+  bool is64bit = N->getValueSizeInBits(0) == 64;
+  const Type *RetTyElement = is64bit ?
+    (const Type *)Type::getInt64Ty(*DAG.getContext()) :
+    (const Type *)Type::getInt32Ty(*DAG.getContext());
+  RetTyParams.push_back(RetTyElement);
+  RetTyParams.push_back(RetTyElement);
+  const Type *RetTy = (const Type
*)StructType::get(*DAG.getContext(), RetTyParams);
+  // Get ABI function name
+  SDValue Callee = DAG.getExternalSymbol(
+    isSigned ? (is64bit ? "__aeabi_ldivmod" : "__aeabi_idivmod") :
+               (is64bit ? "__aeabi_uldivmod" : "__aeabi_uidivmod") , PtrVT);
+  // Lower call
+  std::pair<SDValue, SDValue> CallResult =
+    LowerCallTo(Chain, RetTy, isSigned, !isSigned, false /*vararg*/,
+                /*isInreg=*/false, /*NumFixedArgs=*/0, CallingConv::ARM_AAPCS,
+                /*isTailCall*/false, /*isReturnValueUsed=*/true,
+                Callee, Args, DAG, N->getDebugLoc());
+  // Return second result operand (first operand contains div value)
+  SDNode *ResNode = CallResult.first.getNode();
+  assert(ResNode->getNumOperands() == 2 && "divmod should return two
operands");
+  return ResNode->getOperand(1);
+}
+
 static SDValue LowerVSETCC(SDValue Op, SelectionDAG &DAG) {
   SDValue TmpOp0, TmpOp1;
   bool Invert = false;
@@ -3686,6 +3832,8 @@
   case ISD::SHL:
   case ISD::SRL:
   case ISD::SRA:           return LowerShift(Op.getNode(), DAG, Subtarget);
+  case ISD::SREM:          return LowerREM(Op.getNode(), DAG, true);
+  case ISD::UREM:          return LowerREM(Op.getNode(), DAG, false);
   case ISD::SHL_PARTS:     return LowerShiftLeftParts(Op, DAG);
   case ISD::SRL_PARTS:
   case ISD::SRA_PARTS:     return LowerShiftRightParts(Op, DAG);
@@ -3718,6 +3866,12 @@
   case ISD::SRA:
     Res = LowerShift(N, DAG, Subtarget);
     break;
+  case ISD::UREM:
+    Res = LowerREM(N, DAG, false);
+    break;
+  case ISD::SREM:
+    Res = LowerREM(N, DAG, true);
+    break;
   }
   if (Res.getNode())
     Results.push_back(Res);

Index: lib/Target/ARM/ARMSelectionDAGInfo.h
===================================================================
--- lib/Target/ARM/ARMSelectionDAGInfo.h	(.../import/current)	(revision 160856)
+++ lib/Target/ARM/ARMSelectionDAGInfo.h	(.../trunk)	(revision 160856)
@@ -37,6 +37,13 @@
                                   uint64_t DstSVOff,
                                   const Value *SrcSV,
                                   uint64_t SrcSVOff) const;
+  // Adjust parameters for memset, see RTABI section 4.3.4
+  virtual
+  SDValue EmitTargetCodeForMemset(SelectionDAG &DAG, DebugLoc dl,
+                                  SDValue Chain,
+                                  SDValue Dst, SDValue Src,
+                                  SDValue Size, unsigned Align, bool
isVolatile,
+                                  const Value *DstSV, uint64_t
DstOff) const;
 };

 }

Index: lib/Target/ARM/ARMSelectionDAGInfo.cpp
===================================================================
--- lib/Target/ARM/ARMSelectionDAGInfo.cpp	(.../import/current)	(revision
160856)
+++ lib/Target/ARM/ARMSelectionDAGInfo.cpp	(.../trunk)	(revision 160856)
@@ -13,6 +13,8 @@

 #define DEBUG_TYPE "arm-selectiondag-info"
 #include "ARMTargetMachine.h"
+#include "llvm/DerivedTypes.h"
+#include "llvm/CodeGen/SelectionDAG.h"
 using namespace llvm;

 ARMSelectionDAGInfo::ARMSelectionDAGInfo(const TargetMachine &TM)
@@ -132,3 +134,56 @@
   }
   return DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &TFOps[0], i);
 }
+
+// Adjust parameters for memset, see RTABI section 4.3.4
+SDValue
+ARMSelectionDAGInfo::EmitTargetCodeForMemset(SelectionDAG &DAG, DebugLoc dl,
+                                             SDValue Chain, SDValue Dst,
+                                             SDValue Src, SDValue Size,
+                                             unsigned Align, bool isVolatile,
+                                             const Value *DstSV,
uint64_t DstOff) const
+{
+  // use default for non AAPCS subtargets
+  if (!Subtarget->isAAPCS_ABI()) {
+    return SDValue();
+  }
+
+  const ARMTargetLowering &TLI =
+    *static_cast<const
ARMTargetLowering*>(DAG.getTarget().getTargetLowering());
+
+
+  // Emit __eabi_memset call
+  // ptr
+  const Type *IntPtrTy =
+    cast<Type>(TLI.getTargetData()->getIntPtrType(*DAG.getContext()));
+  TargetLowering::ArgListTy Args;
+  TargetLowering::ArgListEntry Entry;
+  Entry.Node = Dst; Entry.Ty = IntPtrTy;
+  Args.push_back(Entry);
+  // size
+  Entry.Node = Size;
+  Entry.Ty = IntPtrTy;
+  Entry.isSExt = false;
+  Args.push_back(Entry);
+
+  // Extend or truncate the argument to be an i32 value for the call.
+  if (Src.getValueType().bitsGT(MVT::i32))
+    Src = DAG.getNode(ISD::TRUNCATE, dl, MVT::i32, Src);
+  else
+    Src = DAG.getNode(ISD::ZERO_EXTEND, dl, MVT::i32, Src);
+
+  Entry.Node = Src;
+  Entry.Ty = Type::getInt32Ty(*DAG.getContext());
+  Entry.isSExt = true;
+  Args.push_back(Entry);
+
+  std::pair<SDValue,SDValue> CallResult =
+    TLI.LowerCallTo(Chain, Type::getVoidTy(*DAG.getContext()),
+                    false, false, false, false, 0,
+                    TLI.getLibcallCallingConv(RTLIB::MEMSET), false,
+                    /*isReturnValueUsed=*/false,
+                    DAG.getExternalSymbol(TLI.getLibcallName(RTLIB::MEMSET),
+                                          TLI.getPointerTy()),
+                    Args, DAG, dl);
+  return CallResult.second;
+}



More information about the llvm-commits mailing list