<div dir="ltr"><div dir="ltr">Hi Alex,<div><br></div><div>I'm seeing a few test failures after your commit.</div><div>Any ideas on how to fix this?</div><div><br></div><div><br></div><div>Failing Tests (2):<br>    Clang :: Driver/riscv-abi.c<br>    Clang :: Preprocessor/riscv-target-features.c<br clear="all"><div></div></div><div><br></div><div>The failures are:</div><div>/usr/local/google/home/ibiryukov/projects/llvm/clang/test/Preprocessor/riscv-target-features.c:71:18: error: CHECK-DOUBLE: expected string not found in input<br>// CHECK-DOUBLE: __riscv_float_abi_double 1<br>                 ^<br><stdin>:1:1: note: scanning from here<br>#define _ILP32 1<br>^<br><stdin>:12:9: note: possible intended match here<br>#define __CHAR_BIT__ 8<br>        ^<br><br>/usr/local/google/home/ibiryukov/projects/llvm/clang/test/Driver/riscv-abi.c:16:18: error: CHECK-ILP32F: expected string not found in input<br>// CHECK-ILP32F: error: unknown target ABI 'ilp32f'<br>                 ^<br><stdin>:1:1: note: scanning from here<br>Hard-float 'f' ABI can't be used for a target that doesn't support the F instruction set extension (ignoring target-abi)<br>^<br><stdin>:1:93: note: possible intended match here<br>Hard-float 'f' ABI can't be used for a target that doesn't support the F instruction set extension (ignoring target-abi)<br> <br><br></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Jul 18, 2019 at 5:33 PM Alex Bradbury via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: asb<br>
Date: Thu Jul 18 08:33:41 2019<br>
New Revision: 366450<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=366450&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=366450&view=rev</a><br>
Log:<br>
[RISCV] Hard float ABI support<br>
<br>
The RISC-V hard float calling convention requires the frontend to:<br>
<br>
* Detect cases where, once "flattened", a struct can be passed using<br>
int+fp or fp+fp registers under the hard float ABI and coerce to the<br>
appropriate type(s) * Track usage of GPRs and FPRs in order to gate the<br>
above, and to<br>
determine when signext/zeroext attributes must be added to integer<br>
scalars<br>
<br>
This patch attempts to do this in compliance with the documented ABI,<br>
and uses ABIArgInfo::CoerceAndExpand in order to do this. @rjmccall, as<br>
author of that code I've tagged you as reviewer for initial feedback on<br>
my usage.<br>
<br>
Note that a previous version of the ABI indicated that when passing an<br>
int+fp struct using a GPR+FPR, the int would need to be sign or<br>
zero-extended appropriately. GCC never did this and the ABI was changed,<br>
which makes life easier as ABIArgInfo::CoerceAndExpand can't currently<br>
handle sign/zero-extension attributes.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D60456" rel="noreferrer" target="_blank">https://reviews.llvm.org/D60456</a><br>
<br>
Added:<br>
    cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c<br>
    cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c<br>
    cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c<br>
    cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c<br>
    cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c<br>
Modified:<br>
    cfe/trunk/lib/Basic/Targets/RISCV.cpp<br>
    cfe/trunk/lib/Basic/Targets/RISCV.h<br>
    cfe/trunk/lib/CodeGen/TargetInfo.cpp<br>
    cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c<br>
    cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c<br>
    cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c<br>
    cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c<br>
    cfe/trunk/test/Preprocessor/riscv-target-features.c<br>
<br>
Modified: cfe/trunk/lib/Basic/Targets/RISCV.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Targets/RISCV.cpp?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Targets/RISCV.cpp?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Basic/Targets/RISCV.cpp (original)<br>
+++ cfe/trunk/lib/Basic/Targets/RISCV.cpp Thu Jul 18 08:33:41 2019<br>
@@ -65,9 +65,18 @@ void RISCVTargetInfo::getTargetDefines(c<br>
   Builder.defineMacro("__riscv");<br>
   bool Is64Bit = getTriple().getArch() == llvm::Triple::riscv64;<br>
   Builder.defineMacro("__riscv_xlen", Is64Bit ? "64" : "32");<br>
-  // TODO: modify when more code models and ABIs are supported.<br>
+  // TODO: modify when more code models are supported.<br>
   Builder.defineMacro("__riscv_cmodel_medlow");<br>
-  Builder.defineMacro("__riscv_float_abi_soft");<br>
+<br>
+  StringRef ABIName = getABI();<br>
+  if (ABIName == "ilp32f" || ABIName == "lp64f")<br>
+    Builder.defineMacro("__riscv_float_abi_single");<br>
+  else if (ABIName == "ilp32d" || ABIName == "lp64d")<br>
+    Builder.defineMacro("__riscv_float_abi_double");<br>
+  else if (ABIName == "ilp32e")<br>
+    Builder.defineMacro("__riscv_abi_rve");<br>
+  else<br>
+    Builder.defineMacro("__riscv_float_abi_soft");<br>
<br>
   if (HasM) {<br>
     Builder.defineMacro("__riscv_mul");<br>
<br>
Modified: cfe/trunk/lib/Basic/Targets/RISCV.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Targets/RISCV.h?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Targets/RISCV.h?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Basic/Targets/RISCV.h (original)<br>
+++ cfe/trunk/lib/Basic/Targets/RISCV.h Thu Jul 18 08:33:41 2019<br>
@@ -87,8 +87,7 @@ public:<br>
   }<br>
<br>
   bool setABI(const std::string &Name) override {<br>
-    // TODO: support ilp32f and ilp32d ABIs.<br>
-    if (Name == "ilp32") {<br>
+    if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d") {<br>
       ABI = Name;<br>
       return true;<br>
     }<br>
@@ -105,8 +104,7 @@ public:<br>
   }<br>
<br>
   bool setABI(const std::string &Name) override {<br>
-    // TODO: support lp64f and lp64d ABIs.<br>
-    if (Name == "lp64") {<br>
+    if (Name == "lp64" || Name == "lp64f" || Name == "lp64d") {<br>
       ABI = Name;<br>
       return true;<br>
     }<br>
<br>
Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/TargetInfo.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/TargetInfo.cpp Thu Jul 18 08:33:41 2019<br>
@@ -9188,25 +9188,44 @@ static bool getTypeString(SmallStringEnc<br>
 namespace {<br>
 class RISCVABIInfo : public DefaultABIInfo {<br>
 private:<br>
-  unsigned XLen; // Size of the integer ('x') registers in bits.<br>
+  // Size of the integer ('x') registers in bits.<br>
+  unsigned XLen;<br>
+  // Size of the floating point ('f') registers in bits. Note that the target<br>
+  // ISA might have a wider FLen than the selected ABI (e.g. an RV32IF target<br>
+  // with soft float ABI has FLen==0).<br>
+  unsigned FLen;<br>
   static const int NumArgGPRs = 8;<br>
+  static const int NumArgFPRs = 8;<br>
+  bool detectFPCCEligibleStructHelper(QualType Ty, CharUnits CurOff,<br>
+                                      llvm::Type *&Field1Ty,<br>
+                                      CharUnits &Field1Off,<br>
+                                      llvm::Type *&Field2Ty,<br>
+                                      CharUnits &Field2Off) const;<br>
<br>
 public:<br>
-  RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen)<br>
-      : DefaultABIInfo(CGT), XLen(XLen) {}<br>
+  RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, unsigned FLen)<br>
+      : DefaultABIInfo(CGT), XLen(XLen), FLen(FLen) {}<br>
<br>
   // DefaultABIInfo's classifyReturnType and classifyArgumentType are<br>
   // non-virtual, but computeInfo is virtual, so we overload it.<br>
   void computeInfo(CGFunctionInfo &FI) const override;<br>
<br>
-  ABIArgInfo classifyArgumentType(QualType Ty, bool IsFixed,<br>
-                                  int &ArgGPRsLeft) const;<br>
+  ABIArgInfo classifyArgumentType(QualType Ty, bool IsFixed, int &ArgGPRsLeft,<br>
+                                  int &ArgFPRsLeft) const;<br>
   ABIArgInfo classifyReturnType(QualType RetTy) const;<br>
<br>
   Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,<br>
                     QualType Ty) const override;<br>
<br>
   ABIArgInfo extendType(QualType Ty) const;<br>
+<br>
+  bool detectFPCCEligibleStruct(QualType Ty, llvm::Type *&Field1Ty, CharUnits &Field1Off,<br>
+                                llvm::Type *&Field2Ty, CharUnits &Field2Off,<br>
+                                int &NeededArgGPRs, int &NeededArgFPRs) const;<br>
+  ABIArgInfo coerceAndExpandFPCCEligibleStruct(llvm::Type *Field1Ty,<br>
+                                               CharUnits Field1Off,<br>
+                                               llvm::Type *Field2Ty,<br>
+                                               CharUnits Field2Off) const;<br>
 };<br>
 } // end anonymous namespace<br>
<br>
@@ -9228,18 +9247,214 @@ void RISCVABIInfo::computeInfo(CGFunctio<br>
   // different for variadic arguments, we must also track whether we are<br>
   // examining a vararg or not.<br>
   int ArgGPRsLeft = IsRetIndirect ? NumArgGPRs - 1 : NumArgGPRs;<br>
+  int ArgFPRsLeft = FLen ? NumArgFPRs : 0;<br>
   int NumFixedArgs = FI.getNumRequiredArgs();<br>
<br>
   int ArgNum = 0;<br>
   for (auto &ArgInfo : FI.arguments()) {<br>
     bool IsFixed = ArgNum < NumFixedArgs;<br>
-    ArgInfo.info = classifyArgumentType(ArgInfo.type, IsFixed, ArgGPRsLeft);<br>
+    ArgInfo.info =<br>
+        classifyArgumentType(ArgInfo.type, IsFixed, ArgGPRsLeft, ArgFPRsLeft);<br>
     ArgNum++;<br>
   }<br>
 }<br>
<br>
+// Returns true if the struct is a potential candidate for the floating point<br>
+// calling convention. If this function returns true, the caller is<br>
+// responsible for checking that if there is only a single field then that<br>
+// field is a float.<br>
+bool RISCVABIInfo::detectFPCCEligibleStructHelper(QualType Ty, CharUnits CurOff,<br>
+                                                  llvm::Type *&Field1Ty,<br>
+                                                  CharUnits &Field1Off,<br>
+                                                  llvm::Type *&Field2Ty,<br>
+                                                  CharUnits &Field2Off) const {<br>
+  bool IsInt = Ty->isIntegralOrEnumerationType();<br>
+  bool IsFloat = Ty->isRealFloatingType();<br>
+<br>
+  if (IsInt || IsFloat) {<br>
+    uint64_t Size = getContext().getTypeSize(Ty);<br>
+    if (IsInt && Size > XLen)<br>
+      return false;<br>
+    // Can't be eligible if larger than the FP registers. Half precision isn't<br>
+    // currently supported on RISC-V and the ABI hasn't been confirmed, so<br>
+    // default to the integer ABI in that case.<br>
+    if (IsFloat && (Size > FLen || Size < 32))<br>
+      return false;<br>
+    // Can't be eligible if an integer type was already found (int+int pairs<br>
+    // are not eligible).<br>
+    if (IsInt && Field1Ty && Field1Ty->isIntegerTy())<br>
+      return false;<br>
+    if (!Field1Ty) {<br>
+      Field1Ty = CGT.ConvertType(Ty);<br>
+      Field1Off = CurOff;<br>
+      return true;<br>
+    }<br>
+    if (!Field2Ty) {<br>
+      Field2Ty = CGT.ConvertType(Ty);<br>
+      Field2Off = CurOff;<br>
+      return true;<br>
+    }<br>
+    return false;<br>
+  }<br>
+<br>
+  if (auto CTy = Ty->getAs<ComplexType>()) {<br>
+    if (Field1Ty)<br>
+      return false;<br>
+    QualType EltTy = CTy->getElementType();<br>
+    if (getContext().getTypeSize(EltTy) > FLen)<br>
+      return false;<br>
+    Field1Ty = CGT.ConvertType(EltTy);<br>
+    Field1Off = CurOff;<br>
+    assert(CurOff.isZero() && "Unexpected offset for first field");<br>
+    Field2Ty = Field1Ty;<br>
+    Field2Off = Field1Off + getContext().getTypeSizeInChars(EltTy);<br>
+    return true;<br>
+  }<br>
+<br>
+  if (const ConstantArrayType *ATy = getContext().getAsConstantArrayType(Ty)) {<br>
+    uint64_t ArraySize = ATy->getSize().getZExtValue();<br>
+    QualType EltTy = ATy->getElementType();<br>
+    CharUnits EltSize = getContext().getTypeSizeInChars(EltTy);<br>
+    for (uint64_t i = 0; i < ArraySize; ++i) {<br>
+      bool Ret = detectFPCCEligibleStructHelper(EltTy, CurOff, Field1Ty, Field1Off,<br>
+                                                Field2Ty, Field2Off);<br>
+      if (!Ret)<br>
+        return false;<br>
+      CurOff += EltSize;<br>
+    }<br>
+    return true;<br>
+  }<br>
+<br>
+  if (const auto *RTy = Ty->getAs<RecordType>()) {<br>
+    // Structures with either a non-trivial destructor or a non-trivial<br>
+    // copy constructor are not eligible for the FP calling convention.<br>
+    if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT.getCXXABI()))<br>
+      return false;<br>
+    if (isEmptyRecord(getContext(), Ty, true))<br>
+      return true;<br>
+    const RecordDecl *RD = RTy->getDecl();<br>
+    // Unions aren't eligible unless they're empty (which is caught above).<br>
+    if (RD->isUnion())<br>
+      return false;<br>
+    int ZeroWidthBitFieldCount = 0;<br>
+    for (const FieldDecl *FD : RD->fields()) {<br>
+      const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);<br>
+      uint64_t FieldOffInBits = Layout.getFieldOffset(FD->getFieldIndex());<br>
+      QualType QTy = FD->getType();<br>
+      if (FD->isBitField()) {<br>
+        unsigned BitWidth = FD->getBitWidthValue(getContext());<br>
+        // Allow a bitfield with a type greater than XLen as long as the<br>
+        // bitwidth is XLen or less.<br>
+        if (getContext().getTypeSize(QTy) > XLen && BitWidth <= XLen)<br>
+          QTy = getContext().getIntTypeForBitwidth(XLen, false);<br>
+        if (BitWidth == 0) {<br>
+          ZeroWidthBitFieldCount++;<br>
+          continue;<br>
+        }<br>
+      }<br>
+<br>
+      bool Ret = detectFPCCEligibleStructHelper(<br>
+          QTy, CurOff + getContext().toCharUnitsFromBits(FieldOffInBits),<br>
+          Field1Ty, Field1Off, Field2Ty, Field2Off);<br>
+      if (!Ret)<br>
+        return false;<br>
+<br>
+      // As a quirk of the ABI, zero-width bitfields aren't ignored for fp+fp<br>
+      // or int+fp structs, but are ignored for a struct with an fp field and<br>
+      // any number of zero-width bitfields.<br>
+      if (Field2Ty && ZeroWidthBitFieldCount > 0)<br>
+        return false;<br>
+    }<br>
+    return Field1Ty != nullptr;<br>
+  }<br>
+<br>
+  return false;<br>
+}<br>
+<br>
+// Determine if a struct is eligible for passing according to the floating<br>
+// point calling convention (i.e., when flattened it contains a single fp<br>
+// value, fp+fp, or int+fp of appropriate size). If so, NeededArgFPRs and<br>
+// NeededArgGPRs are incremented appropriately.<br>
+bool RISCVABIInfo::detectFPCCEligibleStruct(QualType Ty, llvm::Type *&Field1Ty,<br>
+                                            CharUnits &Field1Off,<br>
+                                            llvm::Type *&Field2Ty,<br>
+                                            CharUnits &Field2Off,<br>
+                                            int &NeededArgGPRs,<br>
+                                            int &NeededArgFPRs) const {<br>
+  Field1Ty = nullptr;<br>
+  Field2Ty = nullptr;<br>
+  NeededArgGPRs = 0;<br>
+  NeededArgFPRs = 0;<br>
+  bool IsCandidate = detectFPCCEligibleStructHelper(<br>
+      Ty, CharUnits::Zero(), Field1Ty, Field1Off, Field2Ty, Field2Off);<br>
+  // Not really a candidate if we have a single int but no float.<br>
+  if (Field1Ty && !Field2Ty && !Field1Ty->isFloatingPointTy())<br>
+    return IsCandidate = false;<br>
+  if (!IsCandidate)<br>
+    return false;<br>
+  if (Field1Ty && Field1Ty->isFloatingPointTy())<br>
+    NeededArgFPRs++;<br>
+  else if (Field1Ty)<br>
+    NeededArgGPRs++;<br>
+  if (Field2Ty && Field2Ty->isFloatingPointTy())<br>
+    NeededArgFPRs++;<br>
+  else if (Field2Ty)<br>
+    NeededArgGPRs++;<br>
+  return IsCandidate;<br>
+}<br>
+<br>
+// Call getCoerceAndExpand for the two-element flattened struct described by<br>
+// Field1Ty, Field1Off, Field2Ty, Field2Off. This method will create an appropriate<br>
+// coerceToType and unpaddedCoerceToType.<br>
+ABIArgInfo RISCVABIInfo::coerceAndExpandFPCCEligibleStruct(<br>
+    llvm::Type *Field1Ty, CharUnits Field1Off, llvm::Type *Field2Ty, CharUnits Field2Off) const {<br>
+  SmallVector<llvm::Type *, 3> CoerceElts;<br>
+  SmallVector<llvm::Type *, 2> UnpaddedCoerceElts;<br>
+  if (!Field1Off.isZero())<br>
+    CoerceElts.push_back(llvm::ArrayType::get(<br>
+        llvm::Type::getInt8Ty(getVMContext()), Field1Off.getQuantity()));<br>
+<br>
+  CoerceElts.push_back(Field1Ty);<br>
+  UnpaddedCoerceElts.push_back(Field1Ty);<br>
+<br>
+  if (!Field2Ty) {<br>
+    return ABIArgInfo::getCoerceAndExpand(<br>
+        llvm::StructType::get(getVMContext(), CoerceElts, !Field1Off.isZero()),<br>
+        UnpaddedCoerceElts[0]);<br>
+  }<br>
+<br>
+  CharUnits Field2Align =<br>
+      CharUnits::fromQuantity(getDataLayout().getABITypeAlignment(Field2Ty));<br>
+  CharUnits Field1Size =<br>
+      CharUnits::fromQuantity(getDataLayout().getTypeStoreSize(Field1Ty));<br>
+  CharUnits Field2OffNoPadNoPack = Field1Size.alignTo(Field2Align);<br>
+<br>
+  CharUnits Padding = CharUnits::Zero();<br>
+  if (Field2Off > Field2OffNoPadNoPack)<br>
+    Padding = Field2Off - Field2OffNoPadNoPack;<br>
+  else if (Field2Off != Field2Align && Field2Off > Field1Size)<br>
+    Padding = Field2Off - Field1Size;<br>
+<br>
+  bool IsPacked = !Field2Off.isMultipleOf(Field2Align);<br>
+<br>
+  if (!Padding.isZero())<br>
+    CoerceElts.push_back(llvm::ArrayType::get(<br>
+        llvm::Type::getInt8Ty(getVMContext()), Padding.getQuantity()));<br>
+<br>
+  CoerceElts.push_back(Field2Ty);<br>
+  UnpaddedCoerceElts.push_back(Field2Ty);<br>
+<br>
+  auto CoerceToType =<br>
+      llvm::StructType::get(getVMContext(), CoerceElts, IsPacked);<br>
+  auto UnpaddedCoerceToType =<br>
+      llvm::StructType::get(getVMContext(), UnpaddedCoerceElts, IsPacked);<br>
+<br>
+  return ABIArgInfo::getCoerceAndExpand(CoerceToType, UnpaddedCoerceToType);<br>
+}<br>
+<br>
 ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,<br>
-                                              int &ArgGPRsLeft) const {<br>
+                                              int &ArgGPRsLeft,<br>
+                                              int &ArgFPRsLeft) const {<br>
   assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");<br>
   Ty = useFirstFieldIfTransparentUnion(Ty);<br>
<br>
@@ -9257,6 +9472,40 @@ ABIArgInfo RISCVABIInfo::classifyArgumen<br>
     return ABIArgInfo::getIgnore();<br>
<br>
   uint64_t Size = getContext().getTypeSize(Ty);<br>
+<br>
+  // Pass floating point values via FPRs if possible.<br>
+  if (IsFixed && Ty->isFloatingType() && FLen >= Size && ArgFPRsLeft) {<br>
+    ArgFPRsLeft--;<br>
+    return ABIArgInfo::getDirect();<br>
+  }<br>
+<br>
+  // Complex types for the hard float ABI must be passed direct rather than<br>
+  // using CoerceAndExpand.<br>
+  if (IsFixed && Ty->isComplexType() && FLen && ArgFPRsLeft >= 2) {<br>
+    QualType EltTy = Ty->getAs<ComplexType>()->getElementType();<br>
+    if (getContext().getTypeSize(EltTy) <= FLen) {<br>
+      ArgFPRsLeft -= 2;<br>
+      return ABIArgInfo::getDirect();<br>
+    }<br>
+  }<br>
+<br>
+  if (IsFixed && FLen && Ty->isStructureOrClassType()) {<br>
+    llvm::Type *Field1Ty = nullptr;<br>
+    llvm::Type *Field2Ty = nullptr;<br>
+    CharUnits Field1Off = CharUnits::Zero();<br>
+    CharUnits Field2Off = CharUnits::Zero();<br>
+    int NeededArgGPRs;<br>
+    int NeededArgFPRs;<br>
+    bool IsCandidate = detectFPCCEligibleStruct(<br>
+        Ty, Field1Ty, Field1Off, Field2Ty, Field2Off, NeededArgGPRs, NeededArgFPRs);<br>
+    if (IsCandidate && NeededArgGPRs <= ArgGPRsLeft &&<br>
+        NeededArgFPRs <= ArgFPRsLeft) {<br>
+      ArgGPRsLeft -= NeededArgGPRs;<br>
+      ArgFPRsLeft -= NeededArgFPRs;<br>
+      return coerceAndExpandFPCCEligibleStruct(Field1Ty, Field1Off, Field2Ty, Field2Off);<br>
+    }<br>
+  }<br>
+<br>
   uint64_t NeededAlign = getContext().getTypeAlign(Ty);<br>
   bool MustUseStack = false;<br>
   // Determine the number of GPRs needed to pass the current argument<br>
@@ -9315,10 +9564,12 @@ ABIArgInfo RISCVABIInfo::classifyReturnT<br>
     return ABIArgInfo::getIgnore();<br>
<br>
   int ArgGPRsLeft = 2;<br>
+  int ArgFPRsLeft = FLen ? 2 : 0;<br>
<br>
   // The rules for return and argument types are the same, so defer to<br>
   // classifyArgumentType.<br>
-  return classifyArgumentType(RetTy, /*IsFixed=*/true, ArgGPRsLeft);<br>
+  return classifyArgumentType(RetTy, /*IsFixed=*/true, ArgGPRsLeft,<br>
+                              ArgFPRsLeft);<br>
 }<br>
<br>
 Address RISCVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,<br>
@@ -9353,8 +9604,9 @@ ABIArgInfo RISCVABIInfo::extendType(Qual<br>
 namespace {<br>
 class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {<br>
 public:<br>
-  RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen)<br>
-      : TargetCodeGenInfo(new RISCVABIInfo(CGT, XLen)) {}<br>
+  RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen,<br>
+                         unsigned FLen)<br>
+      : TargetCodeGenInfo(new RISCVABIInfo(CGT, XLen, FLen)) {}<br>
<br>
   void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,<br>
                            CodeGen::CodeGenModule &CGM) const override {<br>
@@ -9493,9 +9745,16 @@ const TargetCodeGenInfo &CodeGenModule::<br>
     return SetCGInfo(new MSP430TargetCodeGenInfo(Types));<br>
<br>
   case llvm::Triple::riscv32:<br>
-    return SetCGInfo(new RISCVTargetCodeGenInfo(Types, 32));<br>
-  case llvm::Triple::riscv64:<br>
-    return SetCGInfo(new RISCVTargetCodeGenInfo(Types, 64));<br>
+  case llvm::Triple::riscv64: {<br>
+    StringRef ABIStr = getTarget().getABI();<br>
+    unsigned XLen = getTarget().getPointerWidth(0);<br>
+    unsigned ABIFLen = 0;<br>
+    if (ABIStr.endswith("f"))<br>
+      ABIFLen = 32;<br>
+    else if (ABIStr.endswith("d"))<br>
+      ABIFLen = 64;<br>
+    return SetCGInfo(new RISCVTargetCodeGenInfo(Types, XLen, ABIFLen));<br>
+  }<br>
<br>
   case llvm::Triple::systemz: {<br>
     bool HasVector = getTarget().getABI() == "vector";<br>
<br>
Modified: cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c (original)<br>
+++ cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -1,4 +1,6 @@<br>
 // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
<br>
 // This file contains test cases that will have the same output for the ilp32<br>
 // and ilp32f ABIs.<br>
@@ -35,8 +37,8 @@ int f_scalar_stack_1(int32_t a, int64_t<br>
 // the presence of large return values that consume a register due to the need<br>
 // to pass a pointer.<br>
<br>
-// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, i32 %a, i64 %b, i64 %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g)<br>
-struct large f_scalar_stack_2(int32_t a, int64_t b, int64_t c, long double d,<br>
+// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, i32 %a, i64 %b, double %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g)<br>
+struct large f_scalar_stack_2(int32_t a, int64_t b, double c, long double d,<br>
                               uint8_t e, int8_t f, uint8_t g) {<br>
   return (struct large){a, e, f, g};<br>
 }<br>
<br>
Modified: cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c (original)<br>
+++ cfe/trunk/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -1,6 +1,10 @@<br>
 // RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s<br>
 // RUN: %clang_cc1 -triple riscv32 -emit-llvm -fforce-enable-int128 %s -o - \<br>
 // RUN:   | FileCheck %s -check-prefixes=CHECK,CHECK-FORCEINT128<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
<br>
 // This file contains test cases that will have the same output for the ilp32,<br>
 // ilp32f, and ilp32d ABIs.<br>
<br>
Added: cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c?rev=366450&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c?rev=366450&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c (added)<br>
+++ cfe/trunk/test/CodeGen/riscv32-ilp32d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -0,0 +1,282 @@<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+<br>
+#include <stdint.h><br>
+<br>
+// Verify that the tracking of used GPRs and FPRs works correctly by checking<br>
+// that small integers are sign/zero extended when passed in registers.<br>
+<br>
+// Doubles are passed in FPRs, so argument 'i' will be passed zero-extended<br>
+// because it will be passed in a GPR.<br>
+<br>
+// CHECK: define void @f_fpr_tracking(double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, i8 zeroext %i)<br>
+void f_fpr_tracking(double a, double b, double c, double d, double e, double f,<br>
+                    double g, double h, uint8_t i) {}<br>
+<br>
+// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will<br>
+// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are<br>
+// available the widths are <= XLEN and FLEN, and should be expanded to<br>
+// separate arguments in IR. They are passed by the same rules for returns,<br>
+// but will be lowered to simple two-element structs if necessary (as LLVM IR<br>
+// functions cannot return multiple values).<br>
+<br>
+// A struct containing just one floating-point real is passed as though it<br>
+// were a standalone floating-point real.<br>
+<br>
+struct double_s { double f; };<br>
+<br>
+// CHECK: define void @f_double_s_arg(double)<br>
+void f_double_s_arg(struct double_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_double_s()<br>
+struct double_s f_ret_double_s() {<br>
+  return (struct double_s){1.0};<br>
+}<br>
+<br>
+// A struct containing a double and any number of zero-width bitfields is<br>
+// passed as though it were a standalone floating-point real.<br>
+<br>
+struct zbf_double_s { int : 0; double f; };<br>
+struct zbf_double_zbf_s { int : 0; double f; int : 0; };<br>
+<br>
+// CHECK: define void @f_zbf_double_s_arg(double)<br>
+void f_zbf_double_s_arg(struct zbf_double_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_zbf_double_s()<br>
+struct zbf_double_s f_ret_zbf_double_s() {<br>
+  return (struct zbf_double_s){1.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_zbf_double_zbf_s_arg(double)<br>
+void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_zbf_double_zbf_s()<br>
+struct zbf_double_zbf_s f_ret_zbf_double_zbf_s() {<br>
+  return (struct zbf_double_zbf_s){1.0};<br>
+}<br>
+<br>
+// Check that structs containing two floating point values (FLEN <= width) are<br>
+// expanded provided sufficient FPRs are available.<br>
+<br>
+struct double_double_s { double f; double g; };<br>
+struct double_float_s { double f; float g; };<br>
+<br>
+// CHECK: define void @f_double_double_s_arg(double, double)<br>
+void f_double_double_s_arg(struct double_double_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_double_double_s()<br>
+struct double_double_s f_ret_double_double_s() {<br>
+  return (struct double_double_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_float_s_arg(double, float)<br>
+void f_double_float_s_arg(struct double_float_s a) {}<br>
+<br>
+// CHECK: define { double, float } @f_ret_double_float_s()<br>
+struct double_float_s f_ret_double_float_s() {<br>
+  return (struct double_float_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_double_s_arg_insufficient_fprs(float %a, double %b, double %c, double %d, double %e, double %f, double %g, %struct.double_double_s* %h)<br>
+void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d,<br>
+    double e, double f, double g, struct double_double_s h) {}<br>
+<br>
+// Check that structs containing int+double values are expanded, provided<br>
+// sufficient FPRs and GPRs are available. The integer components are neither<br>
+// sign or zero-extended.<br>
+<br>
+struct double_int8_s { double f; int8_t i; };<br>
+struct double_uint8_s { double f; uint8_t i; };<br>
+struct double_int32_s { double f; int32_t i; };<br>
+struct double_int64_s { double f; int64_t i; };<br>
+struct double_int64bf_s { double f; int64_t i : 32; };<br>
+struct double_int8_zbf_s { double f; int8_t i; int : 0; };<br>
+<br>
+// CHECK: define void @f_double_int8_s_arg(double, i8)<br>
+void f_double_int8_s_arg(struct double_int8_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_int8_s()<br>
+struct double_int8_s f_ret_double_int8_s() {<br>
+  return (struct double_int8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_uint8_s_arg(double, i8)<br>
+void f_double_uint8_s_arg(struct double_uint8_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_uint8_s()<br>
+struct double_uint8_s f_ret_double_uint8_s() {<br>
+  return (struct double_uint8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int32_s_arg(double, i32)<br>
+void f_double_int32_s_arg(struct double_int32_s a) {}<br>
+<br>
+// CHECK: define { double, i32 } @f_ret_double_int32_s()<br>
+struct double_int32_s f_ret_double_int32_s() {<br>
+  return (struct double_int32_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int64_s_arg(%struct.double_int64_s* %a)<br>
+void f_double_int64_s_arg(struct double_int64_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_double_int64_s(%struct.double_int64_s* noalias sret %agg.result)<br>
+struct double_int64_s f_ret_double_int64_s() {<br>
+  return (struct double_int64_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int64bf_s_arg(double, i32)<br>
+void f_double_int64bf_s_arg(struct double_int64bf_s a) {}<br>
+<br>
+// CHECK: define { double, i32 } @f_ret_double_int64bf_s()<br>
+struct double_int64bf_s f_ret_double_int64bf_s() {<br>
+  return (struct double_int64bf_s){1.0, 2};<br>
+}<br>
+<br>
+// The zero-width bitfield means the struct can't be passed according to the<br>
+// floating point calling convention.<br>
+<br>
+// CHECK: define void @f_double_int8_zbf_s(double, i8)<br>
+void f_double_int8_zbf_s(struct double_int8_zbf_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_int8_zbf_s()<br>
+struct double_int8_zbf_s f_ret_double_int8_zbf_s() {<br>
+  return (struct double_int8_zbf_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, %struct.double_int8_s* %i)<br>
+void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e,<br>
+                                          int f, int g, int h, struct double_int8_s i) {}<br>
+<br>
+// CHECK: define void @f_struct_double_int8_insufficient_fprs(float %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, %struct.double_int8_s* %i)<br>
+void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d,<br>
+                                           double e, double f, double g, double h, struct double_int8_s i) {}<br>
+<br>
+// Complex floating-point values or structs containing a single complex<br>
+// floating-point value should be passed as if it were an fp+fp struct.<br>
+<br>
+// CHECK: define void @f_doublecomplex(double %a.coerce0, double %a.coerce1)<br>
+void f_doublecomplex(double __complex__ a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublecomplex()<br>
+double __complex__ f_ret_doublecomplex() {<br>
+  return 1.0;<br>
+}<br>
+<br>
+struct doublecomplex_s { double __complex__ c; };<br>
+<br>
+// CHECK: define void @f_doublecomplex_s_arg(double, double)<br>
+void f_doublecomplex_s_arg(struct doublecomplex_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublecomplex_s()<br>
+struct doublecomplex_s f_ret_doublecomplex_s() {<br>
+  return (struct doublecomplex_s){1.0};<br>
+}<br>
+<br>
+// Test single or two-element structs that need flattening. e.g. those<br>
+// containing nested structs, doubles in small arrays, zero-length structs etc.<br>
+<br>
+struct doublearr1_s { double a[1]; };<br>
+<br>
+// CHECK: define void @f_doublearr1_s_arg(double)<br>
+void f_doublearr1_s_arg(struct doublearr1_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_doublearr1_s()<br>
+struct doublearr1_s f_ret_doublearr1_s() {<br>
+  return (struct doublearr1_s){{1.0}};<br>
+}<br>
+<br>
+struct doublearr2_s { double a[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_s_arg(double, double)<br>
+void f_doublearr2_s_arg(struct doublearr2_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_s()<br>
+struct doublearr2_s f_ret_doublearr2_s() {<br>
+  return (struct doublearr2_s){{1.0, 2.0}};<br>
+}<br>
+<br>
+struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky1_s_arg(double, double)<br>
+void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky1_s()<br>
+struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s() {<br>
+  return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky2_s_arg(double, double)<br>
+void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky2_s()<br>
+struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s() {<br>
+  return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky3_s_arg(double, double)<br>
+void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky3_s()<br>
+struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s() {<br>
+  return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky4_s_arg(double, double)<br>
+void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky4_s()<br>
+struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s() {<br>
+  return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}};<br>
+}<br>
+<br>
+// Test structs that should be passed according to the normal integer calling<br>
+// convention.<br>
+<br>
+struct int_double_int_s { int a; double b; int c; };<br>
+<br>
+// CHECK: define void @f_int_double_int_s_arg(%struct.int_double_int_s* %a)<br>
+void f_int_double_int_s_arg(struct int_double_int_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret %agg.result)<br>
+struct int_double_int_s f_ret_int_double_int_s() {<br>
+  return (struct int_double_int_s){1, 2.0, 3};<br>
+}<br>
+<br>
+struct int64_double_s { int64_t a; double b; };<br>
+<br>
+// CHECK: define void @f_int64_double_s_arg(%struct.int64_double_s* %a)<br>
+void f_int64_double_s_arg(struct int64_double_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int64_double_s(%struct.int64_double_s* noalias sret %agg.result)<br>
+struct int64_double_s f_ret_int64_double_s() {<br>
+  return (struct int64_double_s){1, 2.0};<br>
+}<br>
+<br>
+struct char_char_double_s { char a; char b; double c; };<br>
+<br>
+// CHECK-LABEL: define void @f_char_char_double_s_arg(%struct.char_char_double_s* %a)<br>
+void f_char_char_double_s_arg(struct char_char_double_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_char_char_double_s(%struct.char_char_double_s* noalias sret %agg.result)<br>
+struct char_char_double_s f_ret_char_char_double_s() {<br>
+  return (struct char_char_double_s){1, 2, 3.0};<br>
+}<br>
+<br>
+// Unions are always passed according to the integer calling convention, even<br>
+// if they can only contain a double.<br>
+<br>
+union double_u { double a; };<br>
+<br>
+// CHECK: define void @f_double_u_arg(i64 %a.coerce)<br>
+void f_double_u_arg(union double_u a) {}<br>
+<br>
+// CHECK: define i64 @f_ret_double_u()<br>
+union double_u f_ret_double_u() {<br>
+  return (union double_u){1.0};<br>
+}<br>
<br>
Added: cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c?rev=366450&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c?rev=366450&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c (added)<br>
+++ cfe/trunk/test/CodeGen/riscv32-ilp32f-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -0,0 +1,45 @@<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+<br>
+#include <stdint.h><br>
+<br>
+// Doubles are still passed in GPRs, so the 'e' argument will be anyext as<br>
+// GPRs are exhausted.<br>
+<br>
+// CHECK: define void @f_fpr_tracking(double %a, double %b, double %c, double %d, i8 %e)<br>
+void f_fpr_tracking(double a, double b, double c, double d, int8_t e) {}<br>
+<br>
+// Lowering for doubles is unnmodified, as 64 > FLEN.<br>
+<br>
+struct double_s { double d; };<br>
+<br>
+// CHECK: define void @f_double_s_arg(i64 %a.coerce)<br>
+void f_double_s_arg(struct double_s a) {}<br>
+<br>
+// CHECK: define i64 @f_ret_double_s()<br>
+struct double_s f_ret_double_s() {<br>
+  return (struct double_s){1.0};<br>
+}<br>
+<br>
+struct double_double_s { double d; double e; };<br>
+<br>
+// CHECK: define void @f_double_double_s_arg(%struct.double_double_s* %a)<br>
+void f_double_double_s_arg(struct double_double_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_double_double_s(%struct.double_double_s* noalias sret %agg.result)<br>
+struct double_double_s f_ret_double_double_s() {<br>
+  return (struct double_double_s){1.0, 2.0};<br>
+}<br>
+<br>
+struct double_int8_s { double d; int64_t i; };<br>
+<br>
+struct int_double_s { int a; double b; };<br>
+<br>
+// CHECK: define void @f_int_double_s_arg(%struct.int_double_s* %a)<br>
+void f_int_double_s_arg(struct int_double_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int_double_s(%struct.int_double_s* noalias sret %agg.result)<br>
+struct int_double_s f_ret_int_double_s() {<br>
+  return (struct int_double_s){1, 2.0};<br>
+}<br>
+<br>
<br>
Added: cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c?rev=366450&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c?rev=366450&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c (added)<br>
+++ cfe/trunk/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -0,0 +1,275 @@<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+<br>
+#include <stdint.h><br>
+<br>
+// Verify that the tracking of used GPRs and FPRs works correctly by checking<br>
+// that small integers are sign/zero extended when passed in registers.<br>
+<br>
+// Floats are passed in FPRs, so argument 'i' will be passed zero-extended <br>
+// because it will be passed in a GPR.<br>
+<br>
+// CHECK: define void @f_fpr_tracking(float %a, float %b, float %c, float %d, float %e, float %f, float %g, float %h, i8 zeroext %i)<br>
+void f_fpr_tracking(float a, float b, float c, float d, float e, float f,<br>
+                    float g, float h, uint8_t i) {}<br>
+<br>
+// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will <br>
+// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are <br>
+// available the widths are <= XLEN and FLEN, and should be expanded to<br>
+// separate arguments in IR. They are passed by the same rules for returns,<br>
+// but will be lowered to simple two-element structs if necessary (as LLVM IR<br>
+// functions cannot return multiple values).<br>
+<br>
+// A struct containing just one floating-point real is passed as though it <br>
+// were a standalone floating-point real.<br>
+<br>
+struct float_s { float f; };<br>
+<br>
+// CHECK: define void @f_float_s_arg(float)<br>
+void f_float_s_arg(struct float_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_float_s()<br>
+struct float_s f_ret_float_s() {<br>
+  return (struct float_s){1.0};<br>
+}<br>
+<br>
+// A struct containing a float and any number of zero-width bitfields is<br>
+// passed as though it were a standalone floating-point real.<br>
+<br>
+struct zbf_float_s { int : 0; float f; };<br>
+struct zbf_float_zbf_s { int : 0; float f; int : 0; };<br>
+<br>
+// CHECK: define void @f_zbf_float_s_arg(float)<br>
+void f_zbf_float_s_arg(struct zbf_float_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_zbf_float_s()<br>
+struct zbf_float_s f_ret_zbf_float_s() {<br>
+  return (struct zbf_float_s){1.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_zbf_float_zbf_s_arg(float)<br>
+void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_zbf_float_zbf_s()<br>
+struct zbf_float_zbf_s f_ret_zbf_float_zbf_s() {<br>
+  return (struct zbf_float_zbf_s){1.0};<br>
+}<br>
+<br>
+// Check that structs containing two float values (FLEN <= width) are expanded<br>
+// provided sufficient FPRs are available.<br>
+<br>
+struct float_float_s { float f; float g; };<br>
+<br>
+// CHECK: define void @f_float_float_s_arg(float, float)<br>
+void f_float_float_s_arg(struct float_float_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_float_float_s()<br>
+struct float_float_s f_ret_float_float_s() {<br>
+  return (struct float_float_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float %a, float %b, float %c, float %d, float %e, float %f, float %g, [2 x i32] %h.coerce)<br>
+void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, <br>
+    float e, float f, float g, struct float_float_s h) {}<br>
+<br>
+// Check that structs containing int+float values are expanded, provided<br>
+// sufficient FPRs and GPRs are available. The integer components are neither<br>
+// sign or zero-extended.<br>
+<br>
+struct float_int8_s { float f; int8_t i; };<br>
+struct float_uint8_s { float f; uint8_t i; };<br>
+struct float_int32_s { float f; int32_t i; };<br>
+struct float_int64_s { float f; int64_t i; };<br>
+struct float_int64bf_s { float f; int64_t i : 32; };<br>
+struct float_int8_zbf_s { float f; int8_t i; int : 0; };<br>
+<br>
+// CHECK: define void @f_float_int8_s_arg(float, i8)<br>
+void f_float_int8_s_arg(struct float_int8_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_int8_s()<br>
+struct float_int8_s f_ret_float_int8_s() {<br>
+  return (struct float_int8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_uint8_s_arg(float, i8)<br>
+void f_float_uint8_s_arg(struct float_uint8_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_uint8_s()<br>
+struct float_uint8_s f_ret_float_uint8_s() {<br>
+  return (struct float_uint8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int32_s_arg(float, i32)<br>
+void f_float_int32_s_arg(struct float_int32_s a) {}<br>
+<br>
+// CHECK: define { float, i32 } @f_ret_float_int32_s()<br>
+struct float_int32_s f_ret_float_int32_s() {<br>
+  return (struct float_int32_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int64_s_arg(%struct.float_int64_s* %a)<br>
+void f_float_int64_s_arg(struct float_int64_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_float_int64_s(%struct.float_int64_s* noalias sret %agg.result)<br>
+struct float_int64_s f_ret_float_int64_s() {<br>
+  return (struct float_int64_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int64bf_s_arg(float, i32)<br>
+void f_float_int64bf_s_arg(struct float_int64bf_s a) {}<br>
+<br>
+// CHECK: define { float, i32 } @f_ret_float_int64bf_s()<br>
+struct float_int64bf_s f_ret_float_int64bf_s() {<br>
+  return (struct float_int64bf_s){1.0, 2};<br>
+}<br>
+<br>
+// The zero-width bitfield means the struct can't be passed according to the<br>
+// floating point calling convention.<br>
+<br>
+// CHECK: define void @f_float_int8_zbf_s(float, i8)<br>
+void f_float_int8_zbf_s(struct float_int8_zbf_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_int8_zbf_s()<br>
+struct float_int8_zbf_s f_ret_float_int8_zbf_s() {<br>
+  return (struct float_int8_zbf_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, [2 x i32] %i.coerce)<br>
+void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e,<br>
+                                          int f, int g, int h, struct float_int8_s i) {}<br>
+<br>
+// CHECK: define void @f_struct_float_int8_insufficient_fprs(float %a, float %b, float %c, float %d, float %e, float %f, float %g, float %h, [2 x i32] %i.coerce)<br>
+void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d,<br>
+                                           float e, float f, float g, float h, struct float_int8_s i) {}<br>
+<br>
+// Complex floating-point values or structs containing a single complex <br>
+// floating-point value should be passed as if it were an fp+fp struct.<br>
+<br>
+// CHECK: define void @f_floatcomplex(float %a.coerce0, float %a.coerce1)<br>
+void f_floatcomplex(float __complex__ a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatcomplex()<br>
+float __complex__ f_ret_floatcomplex() {<br>
+  return 1.0;<br>
+}<br>
+<br>
+struct floatcomplex_s { float __complex__ c; };<br>
+<br>
+// CHECK: define void @f_floatcomplex_s_arg(float, float)<br>
+void f_floatcomplex_s_arg(struct floatcomplex_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatcomplex_s()<br>
+struct floatcomplex_s f_ret_floatcomplex_s() {<br>
+  return (struct floatcomplex_s){1.0};<br>
+}<br>
+<br>
+// Test single or two-element structs that need flattening. e.g. those <br>
+// containing nested structs, floats in small arrays, zero-length structs etc.<br>
+<br>
+struct floatarr1_s { float a[1]; };<br>
+<br>
+// CHECK: define void @f_floatarr1_s_arg(float)<br>
+void f_floatarr1_s_arg(struct floatarr1_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_floatarr1_s()<br>
+struct floatarr1_s f_ret_floatarr1_s() {<br>
+  return (struct floatarr1_s){{1.0}};<br>
+}<br>
+<br>
+struct floatarr2_s { float a[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_s_arg(float, float)<br>
+void f_floatarr2_s_arg(struct floatarr2_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_s()<br>
+struct floatarr2_s f_ret_floatarr2_s() {<br>
+  return (struct floatarr2_s){{1.0, 2.0}};<br>
+}<br>
+<br>
+struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky1_s_arg(float, float)<br>
+void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky1_s()<br>
+struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s() {<br>
+  return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky2_s_arg(float, float)<br>
+void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky2_s()<br>
+struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s() {<br>
+  return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky3_s_arg(float, float)<br>
+void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky3_s()<br>
+struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s() {<br>
+  return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky4_s_arg(float, float)<br>
+void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky4_s()<br>
+struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s() {<br>
+  return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}};<br>
+}<br>
+<br>
+// Test structs that should be passed according to the normal integer calling<br>
+// convention.<br>
+<br>
+struct int_float_int_s { int a; float b; int c; };<br>
+<br>
+// CHECK: define void @f_int_float_int_s_arg(%struct.int_float_int_s* %a)<br>
+void f_int_float_int_s_arg(struct int_float_int_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int_float_int_s(%struct.int_float_int_s* noalias sret %agg.result)<br>
+struct int_float_int_s f_ret_int_float_int_s() {<br>
+  return (struct int_float_int_s){1, 2.0, 3};<br>
+}<br>
+<br>
+struct int64_float_s { int64_t a; float b; };<br>
+<br>
+// CHECK: define void @f_int64_float_s_arg(%struct.int64_float_s* %a)<br>
+void f_int64_float_s_arg(struct int64_float_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int64_float_s(%struct.int64_float_s* noalias sret %agg.result)<br>
+struct int64_float_s f_ret_int64_float_s() {<br>
+  return (struct int64_float_s){1, 2.0};<br>
+}<br>
+<br>
+struct char_char_float_s { char a; char b; float c; };<br>
+<br>
+// CHECK-LABEL: define void @f_char_char_float_s_arg([2 x i32] %a.coerce)<br>
+void f_char_char_float_s_arg(struct char_char_float_s a) {}<br>
+<br>
+// CHECK: define [2 x i32] @f_ret_char_char_float_s()<br>
+struct char_char_float_s f_ret_char_char_float_s() {<br>
+  return (struct char_char_float_s){1, 2, 3.0};<br>
+}<br>
+<br>
+// Unions are always passed according to the integer calling convention, even <br>
+// if they can only contain a float.<br>
+<br>
+union float_u { float a; };<br>
+<br>
+// CHECK: define void @f_float_u_arg(i32 %a.coerce)<br>
+void f_float_u_arg(union float_u a) {}<br>
+<br>
+// CHECK: define i32 @f_ret_float_u()<br>
+union float_u f_ret_float_u() {<br>
+  return (union float_u){1.0};<br>
+}<br>
<br>
Modified: cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c (original)<br>
+++ cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -1,4 +1,6 @@<br>
 // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
<br>
 // This file contains test cases that will have the same output for the lp64<br>
 // and lp64f ABIs.<br>
<br>
Modified: cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c (original)<br>
+++ cfe/trunk/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -1,4 +1,8 @@<br>
 // RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-abi lp64d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
<br>
 // This file contains test cases that will have the same output for the lp64,<br>
 // lp64f, and lp64d ABIs.<br>
<br>
Added: cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c?rev=366450&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c?rev=366450&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c (added)<br>
+++ cfe/trunk/test/CodeGen/riscv64-lp64d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -0,0 +1,272 @@<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-abi lp64d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+<br>
+#include <stdint.h><br>
+<br>
+// Verify that the tracking of used GPRs and FPRs works correctly by checking<br>
+// that small integers are sign/zero extended when passed in registers.<br>
+<br>
+// Doubles are passed in FPRs, so argument 'i' will be passed zero-extended<br>
+// because it will be passed in a GPR.<br>
+<br>
+// CHECK: define void @f_fpr_tracking(double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, i8 zeroext %i)<br>
+void f_fpr_tracking(double a, double b, double c, double d, double e, double f,<br>
+                    double g, double h, uint8_t i) {}<br>
+<br>
+// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will<br>
+// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are<br>
+// available the widths are <= XLEN and FLEN, and should be expanded to<br>
+// separate arguments in IR. They are passed by the same rules for returns,<br>
+// but will be lowered to simple two-element structs if necessary (as LLVM IR<br>
+// functions cannot return multiple values).<br>
+<br>
+// A struct containing just one floating-point real is passed as though it<br>
+// were a standalone floating-point real.<br>
+<br>
+struct double_s { double f; };<br>
+<br>
+// CHECK: define void @f_double_s_arg(double)<br>
+void f_double_s_arg(struct double_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_double_s()<br>
+struct double_s f_ret_double_s() {<br>
+  return (struct double_s){1.0};<br>
+}<br>
+<br>
+// A struct containing a double and any number of zero-width bitfields is<br>
+// passed as though it were a standalone floating-point real.<br>
+<br>
+struct zbf_double_s { int : 0; double f; };<br>
+struct zbf_double_zbf_s { int : 0; double f; int : 0; };<br>
+<br>
+// CHECK: define void @f_zbf_double_s_arg(double)<br>
+void f_zbf_double_s_arg(struct zbf_double_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_zbf_double_s()<br>
+struct zbf_double_s f_ret_zbf_double_s() {<br>
+  return (struct zbf_double_s){1.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_zbf_double_zbf_s_arg(double)<br>
+void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_zbf_double_zbf_s()<br>
+struct zbf_double_zbf_s f_ret_zbf_double_zbf_s() {<br>
+  return (struct zbf_double_zbf_s){1.0};<br>
+}<br>
+<br>
+// Check that structs containing two floating point values (FLEN <= width) are<br>
+// expanded provided sufficient FPRs are available.<br>
+<br>
+struct double_double_s { double f; double g; };<br>
+struct double_float_s { double f; float g; };<br>
+<br>
+// CHECK: define void @f_double_double_s_arg(double, double)<br>
+void f_double_double_s_arg(struct double_double_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_double_double_s()<br>
+struct double_double_s f_ret_double_double_s() {<br>
+  return (struct double_double_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_float_s_arg(double, float)<br>
+void f_double_float_s_arg(struct double_float_s a) {}<br>
+<br>
+// CHECK: define { double, float } @f_ret_double_float_s()<br>
+struct double_float_s f_ret_double_float_s() {<br>
+  return (struct double_float_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_double_s_arg_insufficient_fprs(float %a, double %b, double %c, double %d, double %e, double %f, double %g, [2 x i64] %h.coerce)<br>
+void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d,<br>
+    double e, double f, double g, struct double_double_s h) {}<br>
+<br>
+// Check that structs containing int+double values are expanded, provided<br>
+// sufficient FPRs and GPRs are available. The integer components are neither<br>
+// sign or zero-extended.<br>
+<br>
+struct double_int8_s { double f; int8_t i; };<br>
+struct double_uint8_s { double f; uint8_t i; };<br>
+struct double_int32_s { double f; int32_t i; };<br>
+struct double_int64_s { double f; int64_t i; };<br>
+struct double_int128bf_s { double f; __int128_t i : 64; };<br>
+struct double_int8_zbf_s { double f; int8_t i; int : 0; };<br>
+<br>
+// CHECK: define void @f_double_int8_s_arg(double, i8)<br>
+void f_double_int8_s_arg(struct double_int8_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_int8_s()<br>
+struct double_int8_s f_ret_double_int8_s() {<br>
+  return (struct double_int8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_uint8_s_arg(double, i8)<br>
+void f_double_uint8_s_arg(struct double_uint8_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_uint8_s()<br>
+struct double_uint8_s f_ret_double_uint8_s() {<br>
+  return (struct double_uint8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int32_s_arg(double, i32)<br>
+void f_double_int32_s_arg(struct double_int32_s a) {}<br>
+<br>
+// CHECK: define { double, i32 } @f_ret_double_int32_s()<br>
+struct double_int32_s f_ret_double_int32_s() {<br>
+  return (struct double_int32_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int64_s_arg(double, i64)<br>
+void f_double_int64_s_arg(struct double_int64_s a) {}<br>
+<br>
+// CHECK: define { double, i64 } @f_ret_double_int64_s()<br>
+struct double_int64_s f_ret_double_int64_s() {<br>
+  return (struct double_int64_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int128bf_s_arg(double, i64)<br>
+void f_double_int128bf_s_arg(struct double_int128bf_s a) {}<br>
+<br>
+// CHECK: define { double, i64 } @f_ret_double_int128bf_s()<br>
+struct double_int128bf_s f_ret_double_int128bf_s() {<br>
+  return (struct double_int128bf_s){1.0, 2};<br>
+}<br>
+<br>
+// The zero-width bitfield means the struct can't be passed according to the<br>
+// floating point calling convention.<br>
+<br>
+// CHECK: define void @f_double_int8_zbf_s(double, i8)<br>
+void f_double_int8_zbf_s(struct double_int8_zbf_s a) {}<br>
+<br>
+// CHECK: define { double, i8 } @f_ret_double_int8_zbf_s()<br>
+struct double_int8_zbf_s f_ret_double_int8_zbf_s() {<br>
+  return (struct double_int8_zbf_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, [2 x i64] %i.coerce)<br>
+void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e,<br>
+                                          int f, int g, int h, struct double_int8_s i) {}<br>
+<br>
+// CHECK: define void @f_struct_double_int8_insufficient_fprs(float %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, [2 x i64] %i.coerce)<br>
+void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d,<br>
+                                           double e, double f, double g, double h, struct double_int8_s i) {}<br>
+<br>
+// Complex floating-point values or structs containing a single complex<br>
+// floating-point value should be passed as if it were an fp+fp struct.<br>
+<br>
+// CHECK: define void @f_doublecomplex(double %a.coerce0, double %a.coerce1)<br>
+void f_doublecomplex(double __complex__ a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublecomplex()<br>
+double __complex__ f_ret_doublecomplex() {<br>
+  return 1.0;<br>
+}<br>
+<br>
+struct doublecomplex_s { double __complex__ c; };<br>
+<br>
+// CHECK: define void @f_doublecomplex_s_arg(double, double)<br>
+void f_doublecomplex_s_arg(struct doublecomplex_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublecomplex_s()<br>
+struct doublecomplex_s f_ret_doublecomplex_s() {<br>
+  return (struct doublecomplex_s){1.0};<br>
+}<br>
+<br>
+// Test single or two-element structs that need flattening. e.g. those<br>
+// containing nested structs, doubles in small arrays, zero-length structs etc.<br>
+<br>
+struct doublearr1_s { double a[1]; };<br>
+<br>
+// CHECK: define void @f_doublearr1_s_arg(double)<br>
+void f_doublearr1_s_arg(struct doublearr1_s a) {}<br>
+<br>
+// CHECK: define double @f_ret_doublearr1_s()<br>
+struct doublearr1_s f_ret_doublearr1_s() {<br>
+  return (struct doublearr1_s){{1.0}};<br>
+}<br>
+<br>
+struct doublearr2_s { double a[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_s_arg(double, double)<br>
+void f_doublearr2_s_arg(struct doublearr2_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_s()<br>
+struct doublearr2_s f_ret_doublearr2_s() {<br>
+  return (struct doublearr2_s){{1.0, 2.0}};<br>
+}<br>
+<br>
+struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky1_s_arg(double, double)<br>
+void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky1_s()<br>
+struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s() {<br>
+  return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky2_s_arg(double, double)<br>
+void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky2_s()<br>
+struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s() {<br>
+  return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky3_s_arg(double, double)<br>
+void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky3_s()<br>
+struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s() {<br>
+  return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_doublearr2_tricky4_s_arg(double, double)<br>
+void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {}<br>
+<br>
+// CHECK: define { double, double } @f_ret_doublearr2_tricky4_s()<br>
+struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s() {<br>
+  return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}};<br>
+}<br>
+<br>
+// Test structs that should be passed according to the normal integer calling<br>
+// convention.<br>
+<br>
+struct int_double_int_s { int a; double b; int c; };<br>
+<br>
+// CHECK: define void @f_int_double_int_s_arg(%struct.int_double_int_s* %a)<br>
+void f_int_double_int_s_arg(struct int_double_int_s a) {}<br>
+<br>
+// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret %agg.result)<br>
+struct int_double_int_s f_ret_int_double_int_s() {<br>
+  return (struct int_double_int_s){1, 2.0, 3};<br>
+}<br>
+<br>
+struct char_char_double_s { char a; char b; double c; };<br>
+<br>
+// CHECK-LABEL: define void @f_char_char_double_s_arg([2 x i64] %a.coerce)<br>
+void f_char_char_double_s_arg(struct char_char_double_s a) {}<br>
+<br>
+// CHECK: define [2 x i64] @f_ret_char_char_double_s()<br>
+struct char_char_double_s f_ret_char_char_double_s() {<br>
+  return (struct char_char_double_s){1, 2, 3.0};<br>
+}<br>
+<br>
+// Unions are always passed according to the integer calling convention, even<br>
+// if they can only contain a double.<br>
+<br>
+union double_u { double a; };<br>
+<br>
+// CHECK: define void @f_double_u_arg(i64 %a.coerce)<br>
+void f_double_u_arg(union double_u a) {}<br>
+<br>
+// CHECK: define i64 @f_ret_double_u()<br>
+union double_u f_ret_double_u() {<br>
+  return (union double_u){1.0};<br>
+}<br>
<br>
Added: cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c?rev=366450&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c?rev=366450&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c (added)<br>
+++ cfe/trunk/test/CodeGen/riscv64-lp64f-lp64d-abi.c Thu Jul 18 08:33:41 2019<br>
@@ -0,0 +1,265 @@<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-abi lp64d -emit-llvm %s -o - \<br>
+// RUN:     | FileCheck %s<br>
+<br>
+#include <stdint.h><br>
+<br>
+// Verify that the tracking of used GPRs and FPRs works correctly by checking<br>
+// that small integers are sign/zero extended when passed in registers.<br>
+<br>
+// Floats are passed in FPRs, so argument 'i' will be passed zero-extended<br>
+// because it will be passed in a GPR.<br>
+<br>
+// CHECK: define void @f_fpr_tracking(float %a, float %b, float %c, float %d, float %e, float %f, float %g, float %h, i8 zeroext %i)<br>
+void f_fpr_tracking(float a, float b, float c, float d, float e, float f,<br>
+                    float g, float h, uint8_t i) {}<br>
+<br>
+// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will<br>
+// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are<br>
+// available the widths are <= XLEN and FLEN, and should be expanded to<br>
+// separate arguments in IR. They are passed by the same rules for returns,<br>
+// but will be lowered to simple two-element structs if necessary (as LLVM IR<br>
+// functions cannot return multiple values).<br>
+<br>
+// A struct containing just one floating-point real is passed as though it<br>
+// were a standalone floating-point real.<br>
+<br>
+struct float_s { float f; };<br>
+<br>
+// CHECK: define void @f_float_s_arg(float)<br>
+void f_float_s_arg(struct float_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_float_s()<br>
+struct float_s f_ret_float_s() {<br>
+  return (struct float_s){1.0};<br>
+}<br>
+<br>
+// A struct containing a float and any number of zero-width bitfields is<br>
+// passed as though it were a standalone floating-point real.<br>
+<br>
+struct zbf_float_s { int : 0; float f; };<br>
+struct zbf_float_zbf_s { int : 0; float f; int : 0; };<br>
+<br>
+// CHECK: define void @f_zbf_float_s_arg(float)<br>
+void f_zbf_float_s_arg(struct zbf_float_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_zbf_float_s()<br>
+struct zbf_float_s f_ret_zbf_float_s() {<br>
+  return (struct zbf_float_s){1.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_zbf_float_zbf_s_arg(float)<br>
+void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_zbf_float_zbf_s()<br>
+struct zbf_float_zbf_s f_ret_zbf_float_zbf_s() {<br>
+  return (struct zbf_float_zbf_s){1.0};<br>
+}<br>
+<br>
+// Check that structs containing two float values (FLEN <= width) are expanded<br>
+// provided sufficient FPRs are available.<br>
+<br>
+struct float_float_s { float f; float g; };<br>
+<br>
+// CHECK: define void @f_float_float_s_arg(float, float)<br>
+void f_float_float_s_arg(struct float_float_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_float_float_s()<br>
+struct float_float_s f_ret_float_float_s() {<br>
+  return (struct float_float_s){1.0, 2.0};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float %a, float %b, float %c, float %d, float %e, float %f, float %g, i64 %h.coerce)<br>
+void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d,<br>
+    float e, float f, float g, struct float_float_s h) {}<br>
+<br>
+// Check that structs containing int+float values are expanded, provided<br>
+// sufficient FPRs and GPRs are available. The integer components are neither<br>
+// sign or zero-extended.<br>
+<br>
+struct float_int8_s { float f; int8_t i; };<br>
+struct float_uint8_s { float f; uint8_t i; };<br>
+struct float_int32_s { float f; int32_t i; };<br>
+struct float_int64_s { float f; int64_t i; };<br>
+struct float_int128bf_s { float f; __int128_t i : 64; };<br>
+struct float_int8_zbf_s { float f; int8_t i; int : 0; };<br>
+<br>
+// CHECK: define void @f_float_int8_s_arg(float, i8)<br>
+void f_float_int8_s_arg(struct float_int8_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_int8_s()<br>
+struct float_int8_s f_ret_float_int8_s() {<br>
+  return (struct float_int8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_uint8_s_arg(float, i8)<br>
+void f_float_uint8_s_arg(struct float_uint8_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_uint8_s()<br>
+struct float_uint8_s f_ret_float_uint8_s() {<br>
+  return (struct float_uint8_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int32_s_arg(float, i32)<br>
+void f_float_int32_s_arg(struct float_int32_s a) {}<br>
+<br>
+// CHECK: define { float, i32 } @f_ret_float_int32_s()<br>
+struct float_int32_s f_ret_float_int32_s() {<br>
+  return (struct float_int32_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int64_s_arg(float, i64)<br>
+void f_float_int64_s_arg(struct float_int64_s a) {}<br>
+<br>
+// CHECK: define { float, i64 } @f_ret_float_int64_s()<br>
+struct float_int64_s f_ret_float_int64_s() {<br>
+  return (struct float_int64_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int128bf_s_arg(float, i64)<br>
+void f_float_int128bf_s_arg(struct float_int128bf_s a) {}<br>
+<br>
+// CHECK: define <{ float, i64 }> @f_ret_float_int128bf_s()<br>
+struct float_int128bf_s f_ret_float_int128bf_s() {<br>
+  return (struct float_int128bf_s){1.0, 2};<br>
+}<br>
+<br>
+// The zero-width bitfield means the struct can't be passed according to the<br>
+// floating point calling convention.<br>
+<br>
+// CHECK: define void @f_float_int8_zbf_s(float, i8)<br>
+void f_float_int8_zbf_s(struct float_int8_zbf_s a) {}<br>
+<br>
+// CHECK: define { float, i8 } @f_ret_float_int8_zbf_s()<br>
+struct float_int8_zbf_s f_ret_float_int8_zbf_s() {<br>
+  return (struct float_int8_zbf_s){1.0, 2};<br>
+}<br>
+<br>
+// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, i64 %i.coerce)<br>
+void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e,<br>
+                                          int f, int g, int h, struct float_int8_s i) {}<br>
+<br>
+// CHECK: define void @f_struct_float_int8_insufficient_fprs(float %a, float %b, float %c, float %d, float %e, float %f, float %g, float %h, i64 %i.coerce)<br>
+void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d,<br>
+                                           float e, float f, float g, float h, struct float_int8_s i) {}<br>
+<br>
+// Complex floating-point values or structs containing a single complex<br>
+// floating-point value should be passed as if it were an fp+fp struct.<br>
+<br>
+// CHECK: define void @f_floatcomplex(float %a.coerce0, float %a.coerce1)<br>
+void f_floatcomplex(float __complex__ a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatcomplex()<br>
+float __complex__ f_ret_floatcomplex() {<br>
+  return 1.0;<br>
+}<br>
+<br>
+struct floatcomplex_s { float __complex__ c; };<br>
+<br>
+// CHECK: define void @f_floatcomplex_s_arg(float, float)<br>
+void f_floatcomplex_s_arg(struct floatcomplex_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatcomplex_s()<br>
+struct floatcomplex_s f_ret_floatcomplex_s() {<br>
+  return (struct floatcomplex_s){1.0};<br>
+}<br>
+<br>
+// Test single or two-element structs that need flattening. e.g. those<br>
+// containing nested structs, floats in small arrays, zero-length structs etc.<br>
+<br>
+struct floatarr1_s { float a[1]; };<br>
+<br>
+// CHECK: define void @f_floatarr1_s_arg(float)<br>
+void f_floatarr1_s_arg(struct floatarr1_s a) {}<br>
+<br>
+// CHECK: define float @f_ret_floatarr1_s()<br>
+struct floatarr1_s f_ret_floatarr1_s() {<br>
+  return (struct floatarr1_s){{1.0}};<br>
+}<br>
+<br>
+struct floatarr2_s { float a[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_s_arg(float, float)<br>
+void f_floatarr2_s_arg(struct floatarr2_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_s()<br>
+struct floatarr2_s f_ret_floatarr2_s() {<br>
+  return (struct floatarr2_s){{1.0, 2.0}};<br>
+}<br>
+<br>
+struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky1_s_arg(float, float)<br>
+void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky1_s()<br>
+struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s() {<br>
+  return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky2_s_arg(float, float)<br>
+void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky2_s()<br>
+struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s() {<br>
+  return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky3_s_arg(float, float)<br>
+void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky3_s()<br>
+struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s() {<br>
+  return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}};<br>
+}<br>
+<br>
+struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; };<br>
+<br>
+// CHECK: define void @f_floatarr2_tricky4_s_arg(float, float)<br>
+void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {}<br>
+<br>
+// CHECK: define { float, float } @f_ret_floatarr2_tricky4_s()<br>
+struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s() {<br>
+  return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}};<br>
+}<br>
+<br>
+// Test structs that should be passed according to the normal integer calling<br>
+// convention.<br>
+<br>
+struct int_float_int_s { int a; float b; int c; };<br>
+<br>
+// CHECK: define void @f_int_float_int_s_arg([2 x i64] %a.coerce)<br>
+void f_int_float_int_s_arg(struct int_float_int_s a) {}<br>
+<br>
+// CHECK: define [2 x i64] @f_ret_int_float_int_s()<br>
+struct int_float_int_s f_ret_int_float_int_s() {<br>
+  return (struct int_float_int_s){1, 2.0, 3};<br>
+}<br>
+<br>
+struct char_char_float_s { char a; char b; float c; };<br>
+<br>
+// CHECK-LABEL: define void @f_char_char_float_s_arg(i64 %a.coerce)<br>
+void f_char_char_float_s_arg(struct char_char_float_s a) {}<br>
+<br>
+// CHECK: define i64 @f_ret_char_char_float_s()<br>
+struct char_char_float_s f_ret_char_char_float_s() {<br>
+  return (struct char_char_float_s){1, 2, 3.0};<br>
+}<br>
+<br>
+// Unions are always passed according to the integer calling convention, even<br>
+// if they can only contain a float.<br>
+<br>
+union float_u { float a; };<br>
+<br>
+// CHECK: define void @f_float_u_arg(i64 %a.coerce)<br>
+void f_float_u_arg(union float_u a) {}<br>
+<br>
+// CHECK: define i64 @f_ret_float_u()<br>
+union float_u f_ret_float_u() {<br>
+  return (union float_u){1.0};<br>
+}<br>
<br>
Modified: cfe/trunk/test/Preprocessor/riscv-target-features.c<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/riscv-target-features.c?rev=366450&r1=366449&r2=366450&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/riscv-target-features.c?rev=366450&r1=366449&r2=366450&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Preprocessor/riscv-target-features.c (original)<br>
+++ cfe/trunk/test/Preprocessor/riscv-target-features.c Thu Jul 18 08:33:41 2019<br>
@@ -47,3 +47,27 @@<br>
 // RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64ic -x c -E -dM %s \<br>
 // RUN: -o - | FileCheck --check-prefix=CHECK-C-EXT %s<br>
 // CHECK-C-EXT: __riscv_compressed 1<br>
+<br>
+// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32ifd -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-SOFT %s<br>
+// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64ifd -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-SOFT %s<br>
+// CHECK-SOFT: __riscv_float_abi_soft 1<br>
+// CHECK-SOFT-NOT: __riscv_float_abi_single<br>
+// CHECK-SOFT-NOT: __riscv_float_abi_double<br>
+<br>
+// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32ifd -mabi=ilp32f -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-SINGLE %s<br>
+// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64ifd -mabi=lp64f -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-SINGLE %s<br>
+// CHECK-SINGLE: __riscv_float_abi_single 1<br>
+// CHECK-SINGLE-NOT: __riscv_float_abi_soft<br>
+// CHECK-SINGLE-NOT: __riscv_float_abi_double<br>
+<br>
+// RUN: %clang -target riscv32-unknown-linux-gnu -march=rv32ifd -mabi=ilp32f -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-DOUBLE %s<br>
+// RUN: %clang -target riscv64-unknown-linux-gnu -march=rv64ifd -mabi=lp64f -x c -E -dM %s \<br>
+// RUN: -o - | FileCheck --check-prefix=CHECK-DOUBLE %s<br>
+// CHECK-DOUBLE: __riscv_float_abi_double 1<br>
+// CHECK-DOUBLE-NOT: __riscv_float_abi_soft<br>
+// CHECK-DOUBLE-NOT: __riscv_float_abi_single<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div>Regards,</div><div>Ilya Biryukov</div></div></div></div></div>