[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
Tue Mar 26 04:11:22 PDT 2024
https://github.com/Long5hot updated https://github.com/llvm/llvm-project/pull/77732
>From 59bd85681c590a5b3e259bfa93d87a80e5362878 Mon Sep 17 00:00:00 2001
From: Kishan Parmar <kparmar2101 at gmail.com>
Date: Tue, 26 Mar 2024 16:40:32 +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.
---
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 | 104 +++++-
clang/lib/Driver/ToolChains/Clang.cpp | 9 +
clang/lib/Frontend/CompilerInvocation.cpp | 8 +
.../CodeGen/PowerPC/ppc32-complex-gnu-abi.c | 306 ++++++++++++++++++
7 files changed, 431 insertions(+), 8 deletions(-)
create mode 100644 clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 340b08dd7e2a33..f4845e9e424c67 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -225,6 +225,8 @@ CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
/// 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 9469a424045bb0..1c9424f65623dd 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -78,6 +78,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 29066ea14280c2..4a5cfc988b8c18 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2577,6 +2577,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 362add30b43500..8df830f150c96f 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -330,8 +330,12 @@ namespace {
class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
bool IsSoftFloatABI;
bool IsRetSmallStructInRegABI;
+ // Size of GPR in bits.
+ static const unsigned RegLen = 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,
@@ -340,12 +344,17 @@ 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);
}
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
@@ -396,12 +405,85 @@ 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);
+
+ if (!(getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_InGPR) ||
+ !ArgGPRsLeft)
+ 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);
+
+ if (Ty->isAnyComplexType()) {
+ // If gr is even set gr = gr + 1 for TypeSize=64.
+ if (TypeSize == 64 && ArgGPRsLeft % 2 == 1)
+ --ArgGPRsLeft;
+
+ if (TypeSize <= RegLen * ArgGPRsLeft) {
+ ArgGPRsLeft -= TypeSize / RegLen;
+ return handleComplex(TypeSize);
+ }
+ }
+
+ // Records with non-trivial destructors/copy-constructors should not be
+ // passed by value.
+ if (isAggregateTypeForABI(Ty))
+ --ArgGPRsLeft;
+ else if (!Ty->isFloatingType()) {
+ // For other primitive types.
+ if (TypeSize == 64) {
+ // If gr is even set gr = gr + 1 for TypeSize=64.
+ if (ArgGPRsLeft % 2 == 1)
+ --ArgGPRsLeft;
+ if (TypeSize <= ArgGPRsLeft * RegLen) {
+ ArgGPRsLeft -= TypeSize / RegLen;
+ }
+ } else
+ --ArgGPRsLeft;
+ }
+
+ 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
@@ -421,6 +503,11 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
}
}
+ bool IsComplexInRegABI =
+ getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_InGPR;
+ if (IsComplexInRegABI && RetTy->isAnyComplexType())
+ return handleComplex(Size);
+
return DefaultABIInfo::classifyReturnType(RetTy);
}
@@ -431,11 +518,12 @@ Address 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,
- /*AllowHigherAlign=*/true);
+ classifyArgumentType(Ty, ArgGPRs).isIndirect(), TI,
+ SlotSize, /*AllowHigherAlign=*/true);
}
const unsigned OverflowLimit = 8;
@@ -1037,8 +1125,8 @@ std::unique_ptr<TargetCodeGenInfo>
CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI) {
bool RetSmallStructInRegABI = PPC32TargetCodeGenInfo::isStructReturnInRegABI(
CGM.getTriple(), CGM.getCodeGenOpts());
- return std::make_unique<PPC32TargetCodeGenInfo>(CGM.getTypes(), SoftFloatABI,
- RetSmallStructInRegABI);
+ return std::make_unique<PPC32TargetCodeGenInfo>(
+ CGM.getTypes(), SoftFloatABI, RetSmallStructInRegABI);
}
std::unique_ptr<TargetCodeGenInfo>
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 3bcacff7724c7d..7471d622a75f67 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5585,6 +5585,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 7bd91d4791ecf0..4733e200d0dcdb 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1651,6 +1651,10 @@ 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);
@@ -2023,6 +2027,10 @@ 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 00000000000000..da08446d9e8a4c
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
@@ -0,0 +1,306 @@
+// 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 @foo1
+// 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] @foo1
+// 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 foo1(_Complex float x) {
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo2
+// 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] @foo2
+// 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 foo2(_Complex double x) {
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo3
+// 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] @foo3
+// 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 foo3(_Complex long double x) {
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo4
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ ppc_fp128, ppc_fp128 }) align 16 [[AGG_RESULT:%.*]], i32 noundef [[X:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[Y:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-DEF-NEXT: [[Y_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[Y_REAL:%.*]] = load ppc_fp128, ptr [[Y_REALP]], align 16
+// CHECK-DEF-NEXT: [[Y_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[Y_IMAG:%.*]] = load ppc_fp128, ptr [[Y_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 [[Y_REAL]], ptr [[AGG_RESULT_REALP]], align 16
+// CHECK-DEF-NEXT: store ppc_fp128 [[Y_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] @foo4
+// CHECK-GNU-SAME: (i32 noundef [[X:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[Y:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-GNU-NEXT: [[Y_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[Y_REAL:%.*]] = load ppc_fp128, ptr [[Y_REALP]], align 16
+// CHECK-GNU-NEXT: [[Y_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 1
+// CHECK-GNU-NEXT: [[Y_IMAG:%.*]] = load ppc_fp128, ptr [[Y_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 [[Y_REAL]], ptr [[RETVAL_REALP]], align 16
+// CHECK-GNU-NEXT: store ppc_fp128 [[Y_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 foo4(int x, _Complex long double y)
+{
+ return y;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo5
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[W:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]], i32 noundef [[Y:%.*]], i64 noundef [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT: [[Z_ADDR:%.*]] = alloca i64, align 8
+// CHECK-DEF-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-DEF-NEXT: store i64 [[Z]], ptr [[Z_ADDR]], align 8
+// CHECK-DEF-NEXT: [[W_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[W_REAL:%.*]] = load double, ptr [[W_REALP]], align 8
+// CHECK-DEF-NEXT: [[W_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[W_IMAG:%.*]] = load double, ptr [[W_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 [[W_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT: store double [[W_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] @foo5
+// CHECK-GNU-SAME: ([4 x i32] noundef [[W_COERCE:%.*]], [1 x i64] noundef [[X_COERCE:%.*]], i32 noundef [[Y:%.*]], i64 noundef [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT: [[W:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT: [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT: [[Z_ADDR:%.*]] = alloca i64, align 8
+// CHECK-GNU-NEXT: store [4 x i32] [[W_COERCE]], ptr [[W]], align 8
+// CHECK-GNU-NEXT: store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
+// CHECK-GNU-NEXT: store i64 [[Z]], ptr [[Z_ADDR]], align 8
+// CHECK-GNU-NEXT: [[W_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[W_REAL:%.*]] = load double, ptr [[W_REALP]], align 8
+// CHECK-GNU-NEXT: [[W_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 1
+// CHECK-GNU-NEXT: [[W_IMAG:%.*]] = load double, ptr [[W_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 [[W_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT: store double [[W_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 foo5(_Complex double w, _Complex float x, int y, long long z)
+{
+ return w;
+}
+
+// CHECK-DEF-LABEL: define dso_local i32 @foo6
+// CHECK-DEF-SAME: (ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[W:%.*]], i32 noundef [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-DEF-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-DEF-NEXT: ret i32 [[TMP0]]
+//
+// CHECK-GNU-LABEL: define dso_local i32 @foo6
+// CHECK-GNU-SAME: ([8 x i32] noundef [[W_COERCE:%.*]], i32 noundef [[X:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[W:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// CHECK-GNU-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT: store [8 x i32] [[W_COERCE]], ptr [[W]], align 16
+// CHECK-GNU-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-GNU-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-GNU-NEXT: ret i32 [[TMP0]]
+//
+int foo6(_Complex long double w, int x)
+{
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo7
+// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], i32 noundef [[X1:%.*]], i64 noundef [[Y1:%.*]], i32 noundef [[X2:%.*]], ptr noundef byval({ double, double }) align 8 [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT: [[Y1_ADDR:%.*]] = alloca i64, align 8
+// CHECK-DEF-NEXT: [[X2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEF-NEXT: store i32 [[X1]], ptr [[X1_ADDR]], align 4
+// CHECK-DEF-NEXT: store i64 [[Y1]], ptr [[Y1_ADDR]], align 8
+// CHECK-DEF-NEXT: store i32 [[X2]], ptr [[X2_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: [[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 [[Z_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT: store double [[Z_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] @foo7
+// CHECK-GNU-SAME: (i32 noundef [[X1:%.*]], i64 noundef [[Y1:%.*]], i32 noundef [[X2:%.*]], ptr noundef byval({ double, double }) align 8 [[Z:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT: [[X1_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT: [[Y1_ADDR:%.*]] = alloca i64, align 8
+// CHECK-GNU-NEXT: [[X2_ADDR:%.*]] = alloca i32, align 4
+// CHECK-GNU-NEXT: store i32 [[X1]], ptr [[X1_ADDR]], align 4
+// CHECK-GNU-NEXT: store i64 [[Y1]], ptr [[Y1_ADDR]], align 8
+// CHECK-GNU-NEXT: store i32 [[X2]], ptr [[X2_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: [[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 [[Z_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT: store double [[Z_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 foo7(int x1, long long y1, int x2, _Complex double z)
+{
+ return z;
+}
More information about the cfe-commits
mailing list