[clang] [clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments (PR #77732)

Kishan Parmar via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 8 03:01:29 PDT 2024


https://github.com/Long5hot updated https://github.com/llvm/llvm-project/pull/77732

>From 2d4ae4906117adcdcd7d08e95ae42ead29bcc9a6 Mon Sep 17 00:00:00 2001
From: Kishan Parmar <kparmar2101 at gmail.com>
Date: Thu, 8 Aug 2024 15:29:56 +0530
Subject: [PATCH] [clang][PowerPC] Add flag to enable compatibility with GNU
 for complex arguments

Fixes : https://github.com/llvm/llvm-project/issues/56023

https://godbolt.org/z/1bsW1sKMs

newFlag : -fcomplex-ppc-gnu-abi

GNU uses GPRs for complex parameters and return values storing for PowerPC-32bit,
which can be enabled which above flag.
Intent of this patch is to make clang compatible with GNU libraries of complex.

Following up with this patch : https://reviews.llvm.org/D146942
---
 clang/include/clang/Basic/CodeGenOptions.def  |   2 +
 clang/include/clang/Basic/CodeGenOptions.h    |   6 +
 clang/include/clang/Driver/Options.td         |   4 +
 clang/lib/CodeGen/Targets/PPC.cpp             |  85 +++-
 clang/lib/Driver/ToolChains/Clang.cpp         |   9 +
 clang/lib/Frontend/CompilerInvocation.cpp     |   6 +
 .../CodeGen/PowerPC/ppc32-complex-gnu-abi.c   | 394 ++++++++++++++++++
 .../ppc32-complex-soft-float-gnu-abi.c        |  95 +++++
 .../test/Driver/ppc32-fcomplex-ppc-gnu-abi.c  |   9 +
 9 files changed, 605 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
 create mode 100644 clang/test/CodeGen/PowerPC/ppc32-complex-soft-float-gnu-abi.c
 create mode 100644 clang/test/Driver/ppc32-fcomplex-ppc-gnu-abi.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 5728ab8d86702..733fb7257c94b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -231,6 +231,8 @@ VALUE_CODEGENOPT(MCDCMaxTVs, 32, 0x7FFFFFFE) ///< MC/DC Maximum test vectors.
 
   /// If -fpcc-struct-return or -freg-struct-return is specified.
 ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
+  /// If -fcomplex-ppc-gnu-abi is specified on ppc32.
+ENUM_CODEGENOPT(ComplexInRegABI, ComplexArgumentConventionKind, 2, CMPLX_OnStack)
 
 CODEGENOPT(RelaxAll          , 1, 0) ///< Relax all machine code instructions.
 CODEGENOPT(RelaxedAliasing   , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index f2a707a8ba8d7..6a53c2b72949e 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -79,6 +79,12 @@ class CodeGenOptions : public CodeGenOptionsBase {
     SRCK_InRegs    // Small structs in registers (-freg-struct-return).
   };
 
+  enum ComplexArgumentConventionKind {
+    CMPLX_OnStack,
+    CMPLX_InGPR, // If -fcomplex-ppc-gnu-abi is specified on ppc32
+    CMPLX_InFPR
+  };
+
   enum ProfileInstrKind {
     ProfileNone,       // Profile instrumentation is turned off.
     ProfileClangInstr, // Clang instrumentation to generate execution counts
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 51ec29f1dc321..340405aafc9f1 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2655,6 +2655,10 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
 
+def fcomplex_ppc_gnu_abi : Flag<["-"], "fcomplex-ppc-gnu-abi">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  DocBrief<"Follow the GNU ABI, pass Complex values in GPRs instead of the stack for PowerPC-32">,
+  HelpText<"Pass Complex values in GPR instead of stack for PowerPC-32">;
+
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
   NegFlag<SetFalse, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index e4155810963eb..89ae806356749 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -327,8 +327,12 @@ namespace {
 class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
   bool IsSoftFloatABI;
   bool IsRetSmallStructInRegABI;
+  // Size of GPR in bits.
+  static const unsigned GPRBits = 32;
+  static const int ArgGPRsNum = 8;
 
   CharUnits getParamTypeAlignment(QualType Ty) const;
+  ABIArgInfo handleComplex(uint64_t &TypeSize) const;
 
 public:
   PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
@@ -337,12 +341,15 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
         IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
 
   ABIArgInfo classifyReturnType(QualType RetTy) const;
+  ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const;
 
   void computeInfo(CGFunctionInfo &FI) const override {
     if (!getCXXABI().classifyReturnType(FI))
       FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+
+    int ArgGPRsLeft = ArgGPRsNum;
     for (auto &I : FI.arguments())
-      I.info = classifyArgumentType(I.type);
+      I.info = classifyArgumentType(I.type, ArgGPRsLeft);
   }
 
   RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
@@ -393,12 +400,74 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
   return CharUnits::fromQuantity(4);
 }
 
+ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(uint64_t &TypeSize) const {
+  llvm::Type *ElemTy;
+  unsigned RegsNeeded; // Registers Needed for Complex.
+
+  // Choice of using llvm::Type::getInt64Ty(getVMContext()) for complex
+  // single-precision floats is based on the ABI ATR-PASS-COMPLEX-IN-GPRS
+  // specification. According to the specification:
+  // - For complex single-precision floats: If the register (gr) is even, it's
+  // incremented by one, and the lower-addressed word of the argument is loaded
+  // into gr, while the higher-addressed word is loaded into gr + 1. Then, gr is
+  // incremented by 2.
+  // - For complex double-precision floats: The words of the argument are loaded
+  // in memory-address order into gr, gr + 1, gr + 2, and gr + 3, with gr being
+  // incremented by 4. Thus, to maintain even alignment and adhere to the ABI
+  // specification, llvm::Type::getInt64Ty(getVMContext()) is used when TypeSize
+  // is 64. Powerpc backend handles this alignment requirement. Specifically,
+  // you can refer to the CC_PPC32_SVR4_Custom_AlignArgRegs method from
+  // PPCCallingconvention.cpp. For more context, refer to the previous
+  // discussion: https://reviews.llvm.org/D146942 and the related LLVM pull
+  // request: #77732
+
+  if (TypeSize == 64) {
+    ElemTy = llvm::Type::getInt64Ty(getVMContext());
+    RegsNeeded = 1;
+  } else {
+    ElemTy = llvm::Type::getInt32Ty(getVMContext());
+    RegsNeeded = TypeSize >> 5;
+  }
+  return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, RegsNeeded));
+}
+
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
+                                                    int &ArgGPRsLeft) const {
+  Ty = useFirstFieldIfTransparentUnion(Ty);
+  bool IsComplex = Ty->isAnyComplexType();
+
+  if ((getCodeGenOpts().getComplexInRegABI() != CodeGenOptions::CMPLX_InGPR) ||
+      !ArgGPRsLeft || (!IsComplex && Ty->isFloatingType() && !IsSoftFloatABI))
+    return DefaultABIInfo::classifyArgumentType(Ty);
+
+  assert(ArgGPRsLeft >= 0 && "Arg GPR must be large or equal than zero");
+  ASTContext &Context = getContext();
+  uint64_t TypeSize = Context.getTypeSize(Ty);
+
+  // For complex type or any other primitive types.
+  if (IsComplex || !isAggregateTypeForABI(Ty)) {
+    // If gr is even set gr = gr + 1 for TypeSize=64.
+    if (TypeSize == 64 && ArgGPRsLeft % 2 == 1)
+      --ArgGPRsLeft;
+
+    if (TypeSize <= GPRBits * ArgGPRsLeft) {
+      ArgGPRsLeft -= TypeSize / GPRBits;
+      if (IsComplex)
+        return handleComplex(TypeSize);
+    }
+  } else
+    --ArgGPRsLeft; // Records with non-trivial destructors/copy-constructors
+                   // should not be passed by value.
+
+  return DefaultABIInfo::classifyArgumentType(Ty);
+}
+
 ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
-  uint64_t Size;
+
+  uint64_t Size = getContext().getTypeSize(RetTy);
 
   // -msvr4-struct-return puts small aggregates in GPR3 and GPR4.
-  if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI &&
-      (Size = getContext().getTypeSize(RetTy)) <= 64) {
+  if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI && Size <= 64) {
     // System V ABI (1995), page 3-22, specified:
     // > A structure or union whose size is less than or equal to 8 bytes
     // > shall be returned in r3 and r4, as if it were first stored in the
@@ -418,6 +487,10 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
     }
   }
 
+  if ((getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_InGPR) &&
+      RetTy->isAnyComplexType())
+    return handleComplex(Size);
+
   return DefaultABIInfo::classifyReturnType(RetTy);
 }
 
@@ -428,10 +501,12 @@ RValue PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
   if (getTarget().getTriple().isOSDarwin()) {
     auto TI = getContext().getTypeInfoInChars(Ty);
     TI.Align = getParamTypeAlignment(Ty);
+    int ArgGPRs = ArgGPRsNum;
 
     CharUnits SlotSize = CharUnits::fromQuantity(4);
     return emitVoidPtrVAArg(CGF, VAList, Ty,
-                            classifyArgumentType(Ty).isIndirect(), TI, SlotSize,
+                            classifyArgumentType(Ty, ArgGPRs).isIndirect(), TI,
+                            SlotSize,
                             /*AllowHigherAlign=*/true, Slot);
   }
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 2054c8fe928e2..d6e8a7609977e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5788,6 +5788,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     }
   }
 
+  if (Arg *A = Args.getLastArg(options::OPT_fcomplex_ppc_gnu_abi)) {
+    if (!TC.getTriple().isPPC32() || !TC.getTriple().isOSBinFormatELF()) {
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << A->getSpelling() << RawTriple.str();
+    } else {
+      CmdArgs.push_back("-fcomplex-ppc-gnu-abi");
+    }
+  }
+
   if (Args.hasFlag(options::OPT_mrtd, options::OPT_mno_rtd, false)) {
     if (Triple.getArch() == llvm::Triple::m68k)
       CmdArgs.push_back("-fdefault-calling-conv=rtdcall");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 225bd6416ce5f..207a26b928953 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1719,6 +1719,9 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
     GenerateArg(Consumer, Opt);
   }
 
+  if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR)
+    GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);
+
   if (Opts.EnableAIXExtendedAltivecABI)
     GenerateArg(Consumer, OPT_mabi_EQ_vec_extabi);
 
@@ -2093,6 +2096,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
     }
   }
 
+  if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi))
+    Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR);
+
   if (Arg *A = Args.getLastArg(OPT_mxcoff_roptr)) {
     if (!T.isOSAIX())
       Diags.Report(diag::err_drv_unsupported_opt_for_target)
diff --git a/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
new file mode 100644
index 0000000000000..192a9e449c2ba
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
@@ -0,0 +1,394 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fcomplex-ppc-gnu-abi \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-GNU
+
+// CHECK-DEF-LABEL: define dso_local void @_cfloat
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ float, float }) align 4 [[AGG_RESULT:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 4
+// CHECK-DEF-NEXT:    store float [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load float, ptr [[AGG_RESULT_REALP1]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load float, ptr [[AGG_RESULT_IMAGP2]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 4
+// CHECK-DEF-NEXT:    store float [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 4
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [1 x i64] @_cfloat
+// CHECK-GNU-SAME: ([1 x i64] noundef [[X_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-GNU-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store float [[X_REAL]], ptr [[RETVAL_REALP]], align 4
+// CHECK-GNU-NEXT:    store float [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [1 x i64], ptr [[RETVAL]], align 4
+// CHECK-GNU-NEXT:    ret [1 x i64] [[TMP0]]
+
+_Complex float _cfloat(_Complex float x) {
+  return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @_cdouble
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @_cdouble
+// CHECK-GNU-SAME: ([4 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    store [4 x i32] [[X_COERCE]], ptr [[X]], align 8
+// CHECK-GNU-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-GNU-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[X_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT:    ret [4 x i32] [[TMP0]]
+//
+_Complex double _cdouble(_Complex double x) {
+  return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @_cldouble
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ ppc_fp128, ppc_fp128 }) align 16 [[AGG_RESULT:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16
+// CHECK-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store ppc_fp128 [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 16
+// CHECK-DEF-NEXT:    store ppc_fp128 [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 16
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load ppc_fp128, ptr [[AGG_RESULT_REALP1]], align 16
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load ppc_fp128, ptr [[AGG_RESULT_IMAGP2]], align 16
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store ppc_fp128 [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 16
+// CHECK-DEF-NEXT:    store ppc_fp128 [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 16
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [8 x i32] @_cldouble
+// CHECK-GNU-SAME: ([8 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-NEXT:    store [8 x i32] [[X_COERCE]], ptr [[X]], align 16
+// CHECK-GNU-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16
+// CHECK-GNU-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store ppc_fp128 [[X_REAL]], ptr [[RETVAL_REALP]], align 16
+// CHECK-GNU-NEXT:    store ppc_fp128 [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [8 x i32], ptr [[RETVAL]], align 16
+// CHECK-GNU-NEXT:    ret [8 x i32] [[TMP0]]
+
+_Complex long double _cldouble(_Complex long double x) {
+  return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @testComplexDouble
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], i32 noundef [[W:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]], ptr noundef byval({ double, double }) align 8 [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[W_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT:    [[TMP:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    store i32 [[W]], ptr [[W_ADDR]], align 4
+// CHECK-DEF-NEXT:    [[Z_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[Z_REAL:%.*]] = load double, ptr [[Z_REALP]], align 8
+// CHECK-DEF-NEXT:    [[Z_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[Z_IMAG:%.*]] = load double, ptr [[Z_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[Z_REAL]], ptr [[INDIRECT_ARG_TEMP_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[Z_IMAG]], ptr [[INDIRECT_ARG_TEMP_IMAGP]], align 8
+// CHECK-DEF-NEXT:    call void @_cdouble(ptr dead_on_unwind writable sret({ double, double }) align 8 [[TMP]], ptr noundef byval({ double, double }) align 8 [[INDIRECT_ARG_TEMP]])
+// CHECK-DEF-NEXT:    [[TMP_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[TMP_REAL:%.*]] = load double, ptr [[TMP_REALP]], align 8
+// CHECK-DEF-NEXT:    [[TMP_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[TMP_IMAG:%.*]] = load double, ptr [[TMP_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[TMP_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[TMP_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @testComplexDouble
+// CHECK-GNU-SAME: (i32 noundef [[W:%.*]], [1 x i64] noundef [[X_COERCE:%.*]], [4 x i32] noundef [[Z_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[Z:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[W_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT:    [[COERCE:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[COERCE1:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-NEXT:    store [4 x i32] [[Z_COERCE]], ptr [[Z]], align 8
+// CHECK-GNU-NEXT:    store i32 [[W]], ptr [[W_ADDR]], align 4
+// CHECK-GNU-NEXT:    [[Z_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[Z_REAL:%.*]] = load double, ptr [[Z_REALP]], align 8
+// CHECK-GNU-NEXT:    [[Z_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[Z_IMAG:%.*]] = load double, ptr [[Z_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[Z_REAL]], ptr [[COERCE_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[Z_IMAG]], ptr [[COERCE_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [4 x i32], ptr [[COERCE]], align 8
+// CHECK-GNU-NEXT:    [[CALL:%.*]] = call [4 x i32] @_cdouble([4 x i32] noundef [[TMP0]])
+// CHECK-GNU-NEXT:    store [4 x i32] [[CALL]], ptr [[COERCE1]], align 8
+// CHECK-GNU-NEXT:    [[COERCE1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE1_REAL:%.*]] = load double, ptr [[COERCE1_REALP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[COERCE1_IMAG:%.*]] = load double, ptr [[COERCE1_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[COERCE1_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[COERCE1_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP1:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT:    ret [4 x i32] [[TMP1]]
+
+_Complex double testComplexDouble(int w, _Complex float x, _Complex double z)
+{
+  return _cdouble(z);
+}
+
+// CHECK-DEF-LABEL: define dso_local void @checkComplexDoubleOnStack
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], i32 noundef [[X1:%.*]], ptr noundef byval({ float, float }) align 4 [[CF:%.*]], i32 noundef [[X2:%.*]], ptr noundef byval({ double, double }) align 8 [[CD:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT:    [[X2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT:    [[TMP:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP:%.*]] = alloca { float, float }, align 4
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    store i32 [[X1]], ptr [[X1_ADDR]], align 4
+// CHECK-DEF-NEXT:    store i32 [[X2]], ptr [[X2_ADDR]], align 4
+// CHECK-DEF-NEXT:    [[TMP0:%.*]] = load i32, ptr [[X2_ADDR]], align 4
+// CHECK-DEF-NEXT:    [[CF_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[CF]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[CF_REAL:%.*]] = load float, ptr [[CF_REALP]], align 4
+// CHECK-DEF-NEXT:    [[CF_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[CF]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[CF_IMAG:%.*]] = load float, ptr [[CF_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[CD_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[CD]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[CD_REAL:%.*]] = load double, ptr [[CD_REALP]], align 8
+// CHECK-DEF-NEXT:    [[CD_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[CD]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[CD_IMAG:%.*]] = load double, ptr [[CD_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[CF_REAL]], ptr [[INDIRECT_ARG_TEMP_REALP]], align 4
+// CHECK-DEF-NEXT:    store float [[CF_IMAG]], ptr [[INDIRECT_ARG_TEMP_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP1]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP1]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[CD_REAL]], ptr [[INDIRECT_ARG_TEMP1_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[CD_IMAG]], ptr [[INDIRECT_ARG_TEMP1_IMAGP]], align 8
+// CHECK-DEF-NEXT:    call void @testComplexDouble(ptr dead_on_unwind writable sret({ double, double }) align 8 [[TMP]], i32 noundef [[TMP0]], ptr noundef byval({ float, float }) align 4 [[INDIRECT_ARG_TEMP]], ptr noundef byval({ double, double }) align 8 [[INDIRECT_ARG_TEMP1]])
+// CHECK-DEF-NEXT:    [[TMP_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[TMP_REAL:%.*]] = load double, ptr [[TMP_REALP]], align 8
+// CHECK-DEF-NEXT:    [[TMP_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[TMP_IMAG:%.*]] = load double, ptr [[TMP_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[TMP_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[TMP_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP2]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP3]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP5:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP4]], align 8
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP5]], align 8
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @checkComplexDoubleOnStack
+// CHECK-GNU-SAME: (i32 noundef [[X1:%.*]], [1 x i64] noundef [[CF_COERCE:%.*]], i32 noundef [[X2:%.*]], ptr noundef byval({ double, double }) align 8 [[CD:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[CF:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[X1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT:    [[X2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT:    [[COERCE:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[COERCE1:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[COERCE2:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    store [1 x i64] [[CF_COERCE]], ptr [[CF]], align 4
+// CHECK-GNU-NEXT:    store i32 [[X1]], ptr [[X1_ADDR]], align 4
+// CHECK-GNU-NEXT:    store i32 [[X2]], ptr [[X2_ADDR]], align 4
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load i32, ptr [[X2_ADDR]], align 4
+// CHECK-GNU-NEXT:    [[CF_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[CF]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[CF_REAL:%.*]] = load float, ptr [[CF_REALP]], align 4
+// CHECK-GNU-NEXT:    [[CF_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[CF]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[CF_IMAG:%.*]] = load float, ptr [[CF_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[CD_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[CD]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[CD_REAL:%.*]] = load double, ptr [[CD_REALP]], align 8
+// CHECK-GNU-NEXT:    [[CD_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[CD]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[CD_IMAG:%.*]] = load double, ptr [[CD_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[COERCE]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[COERCE]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store float [[CF_REAL]], ptr [[COERCE_REALP]], align 4
+// CHECK-GNU-NEXT:    store float [[CF_IMAG]], ptr [[COERCE_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[TMP1:%.*]] = load [1 x i64], ptr [[COERCE]], align 4
+// CHECK-GNU-NEXT:    [[COERCE1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[CD_REAL]], ptr [[COERCE1_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[CD_IMAG]], ptr [[COERCE1_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP2:%.*]] = load [4 x i32], ptr [[COERCE1]], align 8
+// CHECK-GNU-NEXT:    [[CALL:%.*]] = call [4 x i32] @testComplexDouble(i32 noundef [[TMP0]], [1 x i64] noundef [[TMP1]], [4 x i32] noundef [[TMP2]])
+// CHECK-GNU-NEXT:    store [4 x i32] [[CALL]], ptr [[COERCE2]], align 8
+// CHECK-GNU-NEXT:    [[COERCE2_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE2]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE2_REAL:%.*]] = load double, ptr [[COERCE2_REALP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE2_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE2]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[COERCE2_IMAG:%.*]] = load double, ptr [[COERCE2_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[COERCE2_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[COERCE2_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP3:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT:    ret [4 x i32] [[TMP3]]
+
+_Complex double checkComplexDoubleOnStack(int x1, _Complex float cf, int x2, _Complex double cd)
+{
+  return testComplexDouble(x2, cf, cd);
+}
+
+// CHECK-DEF-LABEL: define dso_local void @checkComplexFloatOnStack
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[_CD1:%.*]], ptr noundef byval({ float, float }) align 4 [[_CF1:%.*]], i32 noundef [[Y:%.*]], ptr noundef byval({ float, float }) align 4 [[_CF2:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[Y_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT:    [[TMP:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP:%.*]] = alloca { float, float }, align 4
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1:%.*]] = alloca { double, double }, align 8
+// CHECK-DEF-NEXT:    store i32 [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-DEF-NEXT:    [[TMP0:%.*]] = load i32, ptr [[Y_ADDR]], align 4
+// CHECK-DEF-NEXT:    [[_CF2_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[_CF2]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[_CF2_REAL:%.*]] = load float, ptr [[_CF2_REALP]], align 4
+// CHECK-DEF-NEXT:    [[_CF2_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[_CF2]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[_CF2_IMAG:%.*]] = load float, ptr [[_CF2_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[_CD1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[_CD1]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[_CD1_REAL:%.*]] = load double, ptr [[_CD1_REALP]], align 8
+// CHECK-DEF-NEXT:    [[_CD1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[_CD1]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[_CD1_IMAG:%.*]] = load double, ptr [[_CD1_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[_CF2_REAL]], ptr [[INDIRECT_ARG_TEMP_REALP]], align 4
+// CHECK-DEF-NEXT:    store float [[_CF2_IMAG]], ptr [[INDIRECT_ARG_TEMP_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP1]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[INDIRECT_ARG_TEMP1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP1]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[_CD1_REAL]], ptr [[INDIRECT_ARG_TEMP1_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[_CD1_IMAG]], ptr [[INDIRECT_ARG_TEMP1_IMAGP]], align 8
+// CHECK-DEF-NEXT:    call void @checkComplexDoubleOnStack(ptr dead_on_unwind writable sret({ double, double }) align 8 [[TMP]], i32 noundef [[TMP0]], ptr noundef byval({ float, float }) align 4 [[INDIRECT_ARG_TEMP]], i32 noundef 0, ptr noundef byval({ double, double }) align 8 [[INDIRECT_ARG_TEMP1]])
+// CHECK-DEF-NEXT:    [[TMP_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[TMP_REAL:%.*]] = load double, ptr [[TMP_REALP]], align 8
+// CHECK-DEF-NEXT:    [[TMP_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[TMP]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[TMP_IMAG:%.*]] = load double, ptr [[TMP_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[TMP_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[TMP_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP2]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP3]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP5:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP4]], align 8
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP5]], align 8
+// CHECK-DEF-NEXT:    ret void
+
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @checkComplexFloatOnStack
+// CHECK-GNU-SAME: ([4 x i32] noundef [[_CD1_COERCE:%.*]], [1 x i64] noundef [[_CF1_COERCE:%.*]], i32 noundef [[Y:%.*]], ptr noundef byval({ float, float }) align 4 [[_CF2:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[_CD1:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[_CF1:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[Y_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT:    [[COERCE:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[INDIRECT_ARG_TEMP:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[COERCE1:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    store [4 x i32] [[_CD1_COERCE]], ptr [[_CD1]], align 8
+// CHECK-GNU-NEXT:    store [1 x i64] [[_CF1_COERCE]], ptr [[_CF1]], align 4
+// CHECK-GNU-NEXT:    store i32 [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load i32, ptr [[Y_ADDR]], align 4
+// CHECK-GNU-NEXT:    [[_CF2_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[_CF2]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[_CF2_REAL:%.*]] = load float, ptr [[_CF2_REALP]], align 4
+// CHECK-GNU-NEXT:    [[_CF2_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[_CF2]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[_CF2_IMAG:%.*]] = load float, ptr [[_CF2_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[_CD1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[_CD1]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[_CD1_REAL:%.*]] = load double, ptr [[_CD1_REALP]], align 8
+// CHECK-GNU-NEXT:    [[_CD1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[_CD1]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[_CD1_IMAG:%.*]] = load double, ptr [[_CD1_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[COERCE]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[COERCE]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store float [[_CF2_REAL]], ptr [[COERCE_REALP]], align 4
+// CHECK-GNU-NEXT:    store float [[_CF2_IMAG]], ptr [[COERCE_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[TMP1:%.*]] = load [1 x i64], ptr [[COERCE]], align 4
+// CHECK-GNU-NEXT:    [[INDIRECT_ARG_TEMP_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[INDIRECT_ARG_TEMP_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[INDIRECT_ARG_TEMP]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[_CD1_REAL]], ptr [[INDIRECT_ARG_TEMP_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[_CD1_IMAG]], ptr [[INDIRECT_ARG_TEMP_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[CALL:%.*]] = call [4 x i32] @checkComplexDoubleOnStack(i32 noundef [[TMP0]], [1 x i64] noundef [[TMP1]], i32 noundef 0, ptr noundef byval({ double, double }) align 8 [[INDIRECT_ARG_TEMP]])
+// CHECK-GNU-NEXT:    store [4 x i32] [[CALL]], ptr [[COERCE1]], align 8
+// CHECK-GNU-NEXT:    [[COERCE1_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[COERCE1_REAL:%.*]] = load double, ptr [[COERCE1_REALP]], align 8
+// CHECK-GNU-NEXT:    [[COERCE1_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[COERCE1]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[COERCE1_IMAG:%.*]] = load double, ptr [[COERCE1_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[COERCE1_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[COERCE1_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP2:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT:    ret [4 x i32] [[TMP2]]
+
+_Complex double checkComplexFloatOnStack(_Complex double _cd1, _Complex float _cf1, int y, _Complex float _cf2)
+{
+  return checkComplexDoubleOnStack(y, _cf2, 0, _cd1);
+}
diff --git a/clang/test/CodeGen/PowerPC/ppc32-complex-soft-float-gnu-abi.c b/clang/test/CodeGen/PowerPC/ppc32-complex-soft-float-gnu-abi.c
new file mode 100644
index 0000000000000..b9c7de0a32dc6
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc32-complex-soft-float-gnu-abi.c
@@ -0,0 +1,95 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fcomplex-ppc-gnu-abi \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-GNU-DEF
+// RUN: %clang_cc1 -msoft-float  -mfloat-abi soft -triple powerpc-unknown-linux-gnu -fcomplex-ppc-gnu-abi \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-GNU-SOFT-FLOAT
+
+// CHECK-GNU-DEF-LABEL: define dso_local [8 x i32] @_cldouble
+// CHECK-GNU-DEF-SAME: (float noundef [[F:%.*]], [8 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-GNU-DEF-NEXT:  entry:
+// CHECK-GNU-DEF-NEXT:    [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-DEF-NEXT:    [[X:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-DEF-NEXT:    [[F_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-DEF-NEXT:    store [8 x i32] [[X_COERCE]], ptr [[X]], align 16
+// CHECK-GNU-DEF-NEXT:    store float [[F]], ptr [[F_ADDR]], align 4
+// CHECK-GNU-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-DEF-NEXT:    [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16
+// CHECK-GNU-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-DEF-NEXT:    [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16
+// CHECK-GNU-DEF-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-DEF-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-DEF-NEXT:    store ppc_fp128 [[X_REAL]], ptr [[RETVAL_REALP]], align 16
+// CHECK-GNU-DEF-NEXT:    store ppc_fp128 [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// CHECK-GNU-DEF-NEXT:    [[TMP0:%.*]] = load [8 x i32], ptr [[RETVAL]], align 16
+// CHECK-GNU-DEF-NEXT:    ret [8 x i32] [[TMP0]]
+
+// CHECK-GNU-SOFT-FLOAT-LABEL: define dso_local [8 x i32] @_cldouble
+// CHECK-GNU-SOFT-FLOAT-SAME: (float noundef [[F:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-GNU-SOFT-FLOAT-NEXT:  entry:
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[F_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store float [[F]], ptr [[F_ADDR]], align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store ppc_fp128 [[X_REAL]], ptr [[RETVAL_REALP]], align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store ppc_fp128 [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[TMP0:%.*]] = load [8 x i32], ptr [[RETVAL]], align 16
+// CHECK-GNU-SOFT-FLOAT-NEXT:    ret [8 x i32] [[TMP0]]
+
+_Complex long double _cldouble(float f, _Complex long double x) {
+  return x;
+}
+
+// CHECK-GNU-DEF-LABEL: define dso_local [4 x i32] @testComplexDouble
+// CHECK-GNU-DEF-SAME: (float noundef [[W:%.*]], [1 x i64] noundef [[X_COERCE:%.*]], float noundef [[Y:%.*]], [4 x i32] noundef [[Z_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-DEF-NEXT:  entry:
+// CHECK-GNU-DEF-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-DEF-NEXT:    [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-DEF-NEXT:    [[Z:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-DEF-NEXT:    [[W_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-DEF-NEXT:    [[Y_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-DEF-NEXT:    store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-DEF-NEXT:    store [4 x i32] [[Z_COERCE]], ptr [[Z]], align 8
+// CHECK-GNU-DEF-NEXT:    store float [[W]], ptr [[W_ADDR]], align 4
+// CHECK-GNU-DEF-NEXT:    store float [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-GNU-DEF-NEXT:    [[Z_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 0
+// CHECK-GNU-DEF-NEXT:    [[Z_REAL:%.*]] = load double, ptr [[Z_REALP]], align 8
+// CHECK-GNU-DEF-NEXT:    [[Z_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 1
+// CHECK-GNU-DEF-NEXT:    [[Z_IMAG:%.*]] = load double, ptr [[Z_IMAGP]], align 8
+// CHECK-GNU-DEF-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-DEF-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-DEF-NEXT:    store double [[Z_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-DEF-NEXT:    store double [[Z_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-DEF-NEXT:    [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-DEF-NEXT:    ret [4 x i32] [[TMP0]]
+
+// CHECK-GNU-SOFT-FLOAT-LABEL: define dso_local [4 x i32] @testComplexDouble
+// CHECK-GNU-SOFT-FLOAT-SAME: (float noundef [[W:%.*]], [1 x i64] noundef [[X_COERCE:%.*]], float noundef [[Y:%.*]], ptr noundef byval({ double, double }) align 8 [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-SOFT-FLOAT-NEXT:  entry:
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[W_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[Y_ADDR:%.*]] = alloca float, align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store float [[W]], ptr [[W_ADDR]], align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store float [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[Z_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 0
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[Z_REAL:%.*]] = load double, ptr [[Z_REALP]], align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[Z_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[Z]], i32 0, i32 1
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[Z_IMAG:%.*]] = load double, ptr [[Z_IMAGP]], align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store double [[Z_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    store double [[Z_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-SOFT-FLOAT-NEXT:    ret [4 x i32] [[TMP0]]
+
+_Complex double testComplexDouble(float w, _Complex float x, float y, _Complex double z)
+{
+  return z;
+}
diff --git a/clang/test/Driver/ppc32-fcomplex-ppc-gnu-abi.c b/clang/test/Driver/ppc32-fcomplex-ppc-gnu-abi.c
new file mode 100644
index 0000000000000..335688997e21a
--- /dev/null
+++ b/clang/test/Driver/ppc32-fcomplex-ppc-gnu-abi.c
@@ -0,0 +1,9 @@
+// RUN: %clang -### -S --target=ppc32 -fcomplex-ppc-gnu-abi %s 2>&1
+
+// RUN: not %clang -### --target=ppc64 -fcomplex-ppc-gnu-abi %s 2>&1 \
+// RUN:     | FileCheck %s -check-prefix=CHECK-ERROR
+
+// RUN: not %clang -### --target=ppc32-unknown-unknown-coff -fcomplex-ppc-gnu-abi %s 2>&1 \
+// RUN:     | FileCheck %s -check-prefix=CHECK-ERROR
+
+// CHECK-ERROR: error: unsupported option '-fcomplex-ppc-gnu-abi' for target



More information about the cfe-commits mailing list