[clang] 08396d7 - [Sparc][clang] make `_Complex` ABI GCC-compatible (#212340)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 07:33:31 PDT 2026
Author: Folkert de Vries
Date: 2026-08-05T16:33:25+02:00
New Revision: 08396d76995269fb2bb8f6772b9ccdcae9e7cd23
URL: https://github.com/llvm/llvm-project/commit/08396d76995269fb2bb8f6772b9ccdcae9e7cd23
DIFF: https://github.com/llvm/llvm-project/commit/08396d76995269fb2bb8f6772b9ccdcae9e7cd23.diff
LOG: [Sparc][clang] make `_Complex` ABI GCC-compatible (#212340)
Modify the ABI of `_Complex` so that it matches GCC for all types,
specifically:
- On SPARC, a `_Complex` value with an integer element type is now
passed and
returned packed into the one or two integer registers it fits in,
matching GCC.
Clang previously passed such a value indirectly and returned it with one
part
per register.
`-fclang-abi-compat=23` restores the previous behavior.
- On SPARC64, a `_Complex char` or `_Complex short` is now
right-justified in its slot in the parameter array, like every other
scalar
narrower than a slot, rather than left-justified the way a small struct
is.
`-fclang-abi-compat=23` restores the previous behavior.
Complex integers are a GNU extension, but generally clang is compatible
with GCC. Really, you might as well be, deviating can only bite users.
I've now validated the implementation with
https://github.com/folkertdev/powerpc-complex-abi-validation which
compiles various signatures using `_Complex` with GCC and Clang and
checks that values make it from one side to the other.
related:
- https://github.com/rust-lang/rust/issues/154023
- https://github.com/llvm/llvm-project/pull/208917
- https://github.com/llvm/llvm-project/pull/212119
Added:
clang/test/CodeGen/Sparc/sparc-complex-abi.c
Modified:
clang/docs/ReleaseNotes.md
clang/include/clang/Basic/ABIVersions.def
clang/lib/CodeGen/Targets/Sparc.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ac27b1fc74501..a02ecc41b2292 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -62,6 +62,17 @@ honored, and calls use the caller's features, matching GCC. Per-function
features cannot lower the translation-unit ABI level;
`-fclang-abi-compat=23` restores the previous behavior. (#GH193298)
+- On SPARC, a `_Complex` value with an integer element type is now passed and
+ returned packed into the one or two integer registers it fits in, matching GCC.
+ Clang previously passed such a value indirectly and returned it with one part
+ per register.
+ `-fclang-abi-compat=23` restores the previous behavior. (#GH212340)
+
+- On SPARC64, a `_Complex char` or `_Complex short` is now
+ right-justified in its slot in the parameter array, like every other scalar
+ narrower than a slot, rather than left-justified the way a small struct is.
+ `-fclang-abi-compat=23` restores the previous behavior. (#GH212340)
+
- On MIPS, a `_Complex` value with an integer element type is now returned packed
into a single integer register when it fits in one, matching GCC. A `_Complex char` or
`_Complex short`, and on N32/N64 also a `_Complex int`, is no longer returned
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index 42ee3c3b5f210..e1017a1547773 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -149,6 +149,12 @@ ABI_VER_MAJOR(22)
/// This causes clang to:
/// - Ignore per-function target attributes when determining the x86 AVX ABI
/// level.
+/// - On SPARC, pass a `_Complex` value with an integer element type
+/// indirectly, and return it with one part per integer register, instead of
+/// packing it into the one or two registers it fits in.
+/// - On SPARC64, left-justify a `_Complex` value with an integer element type
+/// that is narrower than a parameter array slot, instead of right-justifying
+/// it the way every other sub-slot scalar is passed.
/// - On MIPS, return a `_Complex` value with an integer element type with one
/// part per integer register, instead of packing it into a single register
/// where it fits.
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 20796bd16d943..e23771653e251 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -22,28 +22,55 @@ using namespace clang::CodeGen;
namespace {
class SparcV8ABIInfo : public DefaultABIInfo {
public:
- SparcV8ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
+ SparcV8ABIInfo(CodeGenTypes &CGT)
+ : DefaultABIInfo(CGT),
+ IsComplexGnuABI(!CGT.getContext().getLangOpts().isCompatibleWith(
+ LangOptions::ClangABI::Ver23)) {}
private:
+ /// Whether how `_Complex` values are passed and returned is GCC-compatible.
+ bool IsComplexGnuABI;
+
+ ABIArgInfo classifyComplexType(const ComplexType *Ty, bool IsRet) const;
ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType Ty) const;
void computeInfo(CGFunctionInfo &FI) const override;
};
} // end anonymous namespace
-ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
- const auto *CT = Ty->getAs<ComplexType>();
- const auto *BT = Ty->getAs<BuiltinType>();
- if (CT)
- BT = CT->getElementType()->getAs<BuiltinType>();
- bool IsLongDouble = BT && BT->getKind() == BuiltinType::LongDouble;
+ABIArgInfo SparcV8ABIInfo::classifyComplexType(const ComplexType *CT,
+ bool IsRet) const {
+ QualType ElementTy = CT->getElementType();
+
+ if (IsComplexGnuABI && ElementTy->isIntegerType()) {
+ // The default path already does the right thing for `long long _Complex`.
+ uint64_t ElementTypeSize = getContext().getTypeSize(ElementTy);
+ if (ElementTypeSize <= 32) {
+ // Coerce to an integer to get the correct scalar-like behavior.
+ return ABIArgInfo::getDirect(
+ llvm::IntegerType::get(getVMContext(), 2 * ElementTypeSize));
+ }
+ }
+
+ // Any other complex value is passed indirectly, but returned in registers.
+ if (!IsRet)
+ return getNaturalAlignIndirect(QualType(CT, 0),
+ getDataLayout().getAllocaAddrSpace());
- // long double _Complex is special in that it should be marked as inreg.
- if (CT)
- return IsLongDouble ? ABIArgInfo::getDirectInReg()
- : ABIArgInfo::getDirect();
+ // long double _Complex is special, it is marked as inreg.
+ const auto *BT = ElementTy->getAs<BuiltinType>();
+ if (BT && BT->getKind() == BuiltinType::LongDouble)
+ return ABIArgInfo::getDirectInReg();
- if (IsLongDouble)
+ return ABIArgInfo::getDirect();
+}
+
+ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
+ if (const auto *CT = Ty->getAs<ComplexType>())
+ return classifyComplexType(CT, /*IsRet=*/true);
+
+ if (const auto *BT = Ty->getAs<BuiltinType>();
+ BT && BT->getKind() == BuiltinType::LongDouble)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
/*ByVal=*/false);
@@ -51,8 +78,11 @@ ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
}
ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
- if (const auto *BT = Ty->getAs<BuiltinType>();
- BT && BT->getKind() == BuiltinType::LongDouble)
+ if (const auto *CT = Ty->getAs<ComplexType>())
+ return classifyComplexType(CT, /*IsRet=*/false);
+
+ const auto *BT = Ty->getAs<BuiltinType>();
+ if (BT && BT->getKind() == BuiltinType::LongDouble)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
return DefaultABIInfo::classifyArgumentType(Ty);
@@ -123,9 +153,15 @@ class SparcV8TargetCodeGenInfo : public TargetCodeGenInfo {
namespace {
class SparcV9ABIInfo : public ABIInfo {
public:
- SparcV9ABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
+ SparcV9ABIInfo(CodeGenTypes &CGT)
+ : ABIInfo(CGT),
+ IsComplexGnuABI(!CGT.getContext().getLangOpts().isCompatibleWith(
+ LangOptions::ClangABI::Ver23)) {}
private:
+ /// Whether how `_Complex` values are passed and returned is GCC-compatible.
+ bool IsComplexGnuABI;
+
ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit,
unsigned &RegOffset) const;
void computeInfo(CGFunctionInfo &FI) const override;
@@ -294,6 +330,23 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
return ABIArgInfo::getExtend(Ty, /*T=*/nullptr, Padding);
}
+ // When being GCC-compatible, cast a complex char, short and int to an integer
+ // type of the right size to get the correct scalar-like behavior. Other
+ // complex types fall through and are treated like a struct containing the
+ // real and imaginary parts, e.g. `{ i64, i64 }` or `{ double, double }`.
+ if (IsComplexGnuABI) {
+ const auto *CT = Ty->getAs<ComplexType>();
+ if (CT && CT->getElementType()->isIntegerType()) {
+ uint64_t ElementTypeSize = Context.getTypeSize(CT->getElementType());
+ if (ElementTypeSize <= 32) {
+ RegOffset += 1;
+ return ABIArgInfo::getDirect(
+ llvm::IntegerType::get(VMContext, 2 * ElementTypeSize),
+ /*Offset=*/0, Padding);
+ }
+ }
+ }
+
// Other non-aggregates go in registers.
if (!isAggregateTypeForABI(Ty)) {
RegOffset += PaddingSlots + SizeSlots;
diff --git a/clang/test/CodeGen/Sparc/sparc-complex-abi.c b/clang/test/CodeGen/Sparc/sparc-complex-abi.c
new file mode 100644
index 0000000000000..55605032be17b
--- /dev/null
+++ b/clang/test/CodeGen/Sparc/sparc-complex-abi.c
@@ -0,0 +1,585 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple sparc-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=V8
+// RUN: %clang_cc1 -triple sparcv9-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=V9
+
+// RUN: %clang_cc1 -triple sparc-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=COMPAT23-V8
+// RUN: %clang_cc1 -triple sparcv9-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=COMPAT23-V9
+
+// Test how SPARC passes and returns `_Complex` values.
+
+// A `_Complex` value with an integer element type is returned packed into whole
+// integer registers. Clang 23 and before instead gave each part a register of
+// its own on v8, and on v9 left-justified a value narrower than a register the
+// way a small struct is returned. The new behavior matches GCC.
+
+// V8-LABEL: define dso_local i16 @complex_char(
+// V8-SAME: i16 noundef [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// V8-NEXT: [[C:%.*]] = alloca { i8, i8 }, align 1
+// V8-NEXT: store i16 [[C_COERCE]], ptr [[C]], align 1
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// V8-NEXT: store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// V8-NEXT: [[TMP0:%.*]] = load i16, ptr [[RETVAL]], align 1
+// V8-NEXT: ret i16 [[TMP0]]
+//
+// V9-LABEL: define dso_local i16 @complex_char(
+// V9-SAME: i16 noundef [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// V9-NEXT: [[C:%.*]] = alloca { i8, i8 }, align 1
+// V9-NEXT: store i16 [[C_COERCE]], ptr [[C]], align 1
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// V9-NEXT: store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// V9-NEXT: [[TMP0:%.*]] = load i16, ptr [[RETVAL]], align 1
+// V9-NEXT: ret i16 [[TMP0]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i8, i8 } @complex_char(
+// COMPAT23-V8-SAME: ptr noundef byval({ i8, i8 }) align 1 [[C:%.*]]) #[[ATTR0:[0-9]+]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// COMPAT23-V8-NEXT: store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { i8, i8 }, ptr [[RETVAL]], align 1
+// COMPAT23-V8-NEXT: ret { i8, i8 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local i64 @complex_char(
+// COMPAT23-V9-SAME: i64 [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V9-NEXT: [[RETVAL_COERCE:%.*]] = alloca i64, align 8
+// COMPAT23-V9-NEXT: [[COERCE_HIGHBITS:%.*]] = lshr i64 [[C_COERCE]], 48
+// COMPAT23-V9-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[COERCE_HIGHBITS]] to i16
+// COMPAT23-V9-NEXT: store i16 [[COERCE_VAL_II]], ptr [[C]], align 1
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// COMPAT23-V9-NEXT: store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// COMPAT23-V9-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 1 [[RETVAL]], i64 2, i1 false)
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL_COERCE]], align 8
+// COMPAT23-V9-NEXT: ret i64 [[TMP0]]
+//
+_Complex char complex_char(_Complex char c) { return c; }
+
+// V8-LABEL: define dso_local i32 @complex_short(
+// V8-SAME: i32 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// V8-NEXT: [[C:%.*]] = alloca { i16, i16 }, align 2
+// V8-NEXT: store i32 [[C_COERCE]], ptr [[C]], align 2
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// V8-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
+// V8-NEXT: [[TMP0:%.*]] = load i32, ptr [[RETVAL]], align 2
+// V8-NEXT: ret i32 [[TMP0]]
+//
+// V9-LABEL: define dso_local i32 @complex_short(
+// V9-SAME: i32 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// V9-NEXT: [[C:%.*]] = alloca { i16, i16 }, align 2
+// V9-NEXT: store i32 [[C_COERCE]], ptr [[C]], align 2
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// V9-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
+// V9-NEXT: [[TMP0:%.*]] = load i32, ptr [[RETVAL]], align 2
+// V9-NEXT: ret i32 [[TMP0]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i16, i16 } @complex_short(
+// COMPAT23-V8-SAME: ptr noundef byval({ i16, i16 }) align 2 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// COMPAT23-V8-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { i16, i16 }, ptr [[RETVAL]], align 2
+// COMPAT23-V8-NEXT: ret { i16, i16 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local i64 @complex_short(
+// COMPAT23-V9-SAME: i64 [[C_COERCE:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { i16, i16 }, align 2
+// COMPAT23-V9-NEXT: [[RETVAL_COERCE:%.*]] = alloca i64, align 8
+// COMPAT23-V9-NEXT: [[COERCE_HIGHBITS:%.*]] = lshr i64 [[C_COERCE]], 32
+// COMPAT23-V9-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[COERCE_HIGHBITS]] to i32
+// COMPAT23-V9-NEXT: store i32 [[COERCE_VAL_II]], ptr [[C]], align 2
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// COMPAT23-V9-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
+// COMPAT23-V9-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 2 [[RETVAL]], i64 4, i1 false)
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL_COERCE]], align 8
+// COMPAT23-V9-NEXT: ret i64 [[TMP0]]
+//
+_Complex short complex_short(_Complex short c) { return c; }
+
+// V8-LABEL: define dso_local i64 @complex_int(
+// V8-SAME: i64 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// V8-NEXT: [[C:%.*]] = alloca { i32, i32 }, align 4
+// V8-NEXT: store i64 [[C_COERCE]], ptr [[C]], align 4
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// V8-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// V8-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL]], align 4
+// V8-NEXT: ret i64 [[TMP0]]
+//
+// V9-LABEL: define dso_local i64 @complex_int(
+// V9-SAME: i64 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// V9-NEXT: [[C:%.*]] = alloca { i32, i32 }, align 4
+// V9-NEXT: store i64 [[C_COERCE]], ptr [[C]], align 4
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// V9-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// V9-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL]], align 4
+// V9-NEXT: ret i64 [[TMP0]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i32, i32 } @complex_int(
+// COMPAT23-V8-SAME: ptr noundef byval({ i32, i32 }) align 4 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// COMPAT23-V8-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { i32, i32 }, ptr [[RETVAL]], align 4
+// COMPAT23-V8-NEXT: ret { i32, i32 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local i64 @complex_int(
+// COMPAT23-V9-SAME: i64 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { i32, i32 }, align 4
+// COMPAT23-V9-NEXT: store i64 [[C_COERCE]], ptr [[C]], align 4
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// COMPAT23-V9-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL]], align 4
+// COMPAT23-V9-NEXT: ret i64 [[TMP0]]
+//
+_Complex int complex_int(_Complex int c) { return c; }
+
+// V8-LABEL: define dso_local i64 @complex_long(
+// V8-SAME: i64 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// V8-NEXT: [[C:%.*]] = alloca { i32, i32 }, align 4
+// V8-NEXT: store i64 [[C_COERCE]], ptr [[C]], align 4
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// V8-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// V8-NEXT: [[TMP0:%.*]] = load i64, ptr [[RETVAL]], align 4
+// V8-NEXT: ret i64 [[TMP0]]
+//
+// V9-LABEL: define dso_local { i64, i64 } @complex_long(
+// V9-SAME: i64 noundef [[C_COERCE0:%.*]], i64 noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// V9-NEXT: [[C:%.*]] = alloca { i64, i64 }, align 8
+// V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: store i64 [[C_COERCE0]], ptr [[TMP0]], align 8
+// V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: store i64 [[C_COERCE1]], ptr [[TMP1]], align 8
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V9-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V9-NEXT: [[TMP2:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// V9-NEXT: ret { i64, i64 } [[TMP2]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i32, i32 } @complex_long(
+// COMPAT23-V8-SAME: ptr noundef byval({ i32, i32 }) align 4 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { i32, i32 }, align 4
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load i32, ptr [[C_REALP]], align 4
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load i32, ptr [[C_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store i32 [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// COMPAT23-V8-NEXT: store i32 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { i32, i32 }, ptr [[RETVAL]], align 4
+// COMPAT23-V8-NEXT: ret { i32, i32 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local { i64, i64 } @complex_long(
+// COMPAT23-V9-SAME: i64 noundef [[C_COERCE0:%.*]], i64 noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { i64, i64 }, align 8
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: store i64 [[C_COERCE0]], ptr [[TMP0]], align 8
+// COMPAT23-V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i64 [[C_COERCE1]], ptr [[TMP1]], align 8
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V9-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[TMP2:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// COMPAT23-V9-NEXT: ret { i64, i64 } [[TMP2]]
+//
+_Complex long complex_long(_Complex long c) { return c; }
+
+// V8-LABEL: define dso_local { i64, i64 } @complex_long_long(
+// V8-SAME: ptr noundef byval({ i64, i64 }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V8-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V8-NEXT: [[TMP0:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// V8-NEXT: ret { i64, i64 } [[TMP0]]
+//
+// V9-LABEL: define dso_local { i64, i64 } @complex_long_long(
+// V9-SAME: i64 noundef [[C_COERCE0:%.*]], i64 noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// V9-NEXT: [[C:%.*]] = alloca { i64, i64 }, align 8
+// V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: store i64 [[C_COERCE0]], ptr [[TMP0]], align 8
+// V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: store i64 [[C_COERCE1]], ptr [[TMP1]], align 8
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V9-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V9-NEXT: [[TMP2:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// V9-NEXT: ret { i64, i64 } [[TMP2]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i64, i64 } @complex_long_long(
+// COMPAT23-V8-SAME: ptr noundef byval({ i64, i64 }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V8-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// COMPAT23-V8-NEXT: ret { i64, i64 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local { i64, i64 } @complex_long_long(
+// COMPAT23-V9-SAME: i64 noundef [[C_COERCE0:%.*]], i64 noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { i64, i64 }, align 8
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { i64, i64 }, align 8
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: store i64 [[C_COERCE0]], ptr [[TMP0]], align 8
+// COMPAT23-V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i64 [[C_COERCE1]], ptr [[TMP1]], align 8
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load i64, ptr [[C_REALP]], align 8
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load i64, ptr [[C_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store i64 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V9-NEXT: store i64 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[TMP2:%.*]] = load { i64, i64 }, ptr [[RETVAL]], align 8
+// COMPAT23-V9-NEXT: ret { i64, i64 } [[TMP2]]
+//
+_Complex long long complex_long_long(_Complex long long c) { return c; }
+
+// V8-LABEL: define dso_local { float, float } @complex_float(
+// V8-SAME: ptr noundef byval({ float, float }) align 4 [[C:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load float, ptr [[C_REALP]], align 4
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store float [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// V8-NEXT: store float [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// V8-NEXT: [[TMP0:%.*]] = load { float, float }, ptr [[RETVAL]], align 4
+// V8-NEXT: ret { float, float } [[TMP0]]
+//
+// V9-LABEL: define dso_local inreg { float, float } @complex_float(
+// V9-SAME: float inreg noundef [[C_COERCE0:%.*]], float inreg noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// V9-NEXT: [[C:%.*]] = alloca { float, float }, align 4
+// V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: store float [[C_COERCE0]], ptr [[TMP0]], align 4
+// V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: store float [[C_COERCE1]], ptr [[TMP1]], align 4
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load float, ptr [[C_REALP]], align 4
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store float [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// V9-NEXT: store float [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// V9-NEXT: [[TMP2:%.*]] = load { float, float }, ptr [[RETVAL]], align 4
+// V9-NEXT: ret { float, float } [[TMP2]]
+//
+// COMPAT23-V8-LABEL: define dso_local { float, float } @complex_float(
+// COMPAT23-V8-SAME: ptr noundef byval({ float, float }) align 4 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load float, ptr [[C_REALP]], align 4
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store float [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// COMPAT23-V8-NEXT: store float [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { float, float }, ptr [[RETVAL]], align 4
+// COMPAT23-V8-NEXT: ret { float, float } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local inreg { float, float } @complex_float(
+// COMPAT23-V9-SAME: float inreg noundef [[C_COERCE0:%.*]], float inreg noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { float, float }, align 4
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: store float [[C_COERCE0]], ptr [[TMP0]], align 4
+// COMPAT23-V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store float [[C_COERCE1]], ptr [[TMP1]], align 4
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load float, ptr [[C_REALP]], align 4
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load float, ptr [[C_IMAGP]], align 4
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store float [[C_REAL]], ptr [[RETVAL_REALP]], align 4
+// COMPAT23-V9-NEXT: store float [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// COMPAT23-V9-NEXT: [[TMP2:%.*]] = load { float, float }, ptr [[RETVAL]], align 4
+// COMPAT23-V9-NEXT: ret { float, float } [[TMP2]]
+//
+_Complex float complex_float(_Complex float c) { return c; }
+
+// V8-LABEL: define dso_local { double, double } @complex_double(
+// V8-SAME: ptr noundef byval({ double, double }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load double, ptr [[C_REALP]], align 8
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load double, ptr [[C_IMAGP]], align 8
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store double [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V8-NEXT: store double [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V8-NEXT: [[TMP0:%.*]] = load { double, double }, ptr [[RETVAL]], align 8
+// V8-NEXT: ret { double, double } [[TMP0]]
+//
+// V9-LABEL: define dso_local { double, double } @complex_double(
+// V9-SAME: double noundef [[C_COERCE0:%.*]], double noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// V9-NEXT: [[C:%.*]] = alloca { double, double }, align 8
+// V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: store double [[C_COERCE0]], ptr [[TMP0]], align 8
+// V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: store double [[C_COERCE1]], ptr [[TMP1]], align 8
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load double, ptr [[C_REALP]], align 8
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load double, ptr [[C_IMAGP]], align 8
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store double [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V9-NEXT: store double [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V9-NEXT: [[TMP2:%.*]] = load { double, double }, ptr [[RETVAL]], align 8
+// V9-NEXT: ret { double, double } [[TMP2]]
+//
+// COMPAT23-V8-LABEL: define dso_local { double, double } @complex_double(
+// COMPAT23-V8-SAME: ptr noundef byval({ double, double }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load double, ptr [[C_REALP]], align 8
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load double, ptr [[C_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store double [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V8-NEXT: store double [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { double, double }, ptr [[RETVAL]], align 8
+// COMPAT23-V8-NEXT: ret { double, double } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local { double, double } @complex_double(
+// COMPAT23-V9-SAME: double noundef [[C_COERCE0:%.*]], double noundef [[C_COERCE1:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// COMPAT23-V9-NEXT: [[C:%.*]] = alloca { double, double }, align 8
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: store double [[C_COERCE0]], ptr [[TMP0]], align 8
+// COMPAT23-V9-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store double [[C_COERCE1]], ptr [[TMP1]], align 8
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load double, ptr [[C_REALP]], align 8
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load double, ptr [[C_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store double [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V9-NEXT: store double [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V9-NEXT: [[TMP2:%.*]] = load { double, double }, ptr [[RETVAL]], align 8
+// COMPAT23-V9-NEXT: ret { double, double } [[TMP2]]
+//
+_Complex double complex_double(_Complex double c) { return c; }
+
+// V8-LABEL: define dso_local inreg { fp128, fp128 } @complex_long_double(
+// V8-SAME: ptr noundef byval({ fp128, fp128 }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// V8-NEXT: [[ENTRY:.*:]]
+// V8-NEXT: [[RETVAL:%.*]] = alloca { fp128, fp128 }, align 8
+// V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT: [[C_REAL:%.*]] = load fp128, ptr [[C_REALP]], align 8
+// V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT: [[C_IMAG:%.*]] = load fp128, ptr [[C_IMAGP]], align 8
+// V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT: store fp128 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// V8-NEXT: store fp128 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// V8-NEXT: [[TMP0:%.*]] = load { fp128, fp128 }, ptr [[RETVAL]], align 8
+// V8-NEXT: ret { fp128, fp128 } [[TMP0]]
+//
+// V9-LABEL: define dso_local { fp128, fp128 } @complex_long_double(
+// V9-SAME: ptr noundef align 16 dead_on_return [[C:%.*]]) #[[ATTR0]] {
+// V9-NEXT: [[ENTRY:.*:]]
+// V9-NEXT: [[RETVAL:%.*]] = alloca { fp128, fp128 }, align 16
+// V9-NEXT: [[C_INDIRECT_ADDR:%.*]] = alloca ptr, align 8
+// V9-NEXT: store ptr [[C]], ptr [[C_INDIRECT_ADDR]], align 8
+// V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT: [[C_REAL:%.*]] = load fp128, ptr [[C_REALP]], align 16
+// V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT: [[C_IMAG:%.*]] = load fp128, ptr [[C_IMAGP]], align 16
+// V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT: store fp128 [[C_REAL]], ptr [[RETVAL_REALP]], align 16
+// V9-NEXT: store fp128 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// V9-NEXT: [[TMP0:%.*]] = load { fp128, fp128 }, ptr [[RETVAL]], align 16
+// V9-NEXT: ret { fp128, fp128 } [[TMP0]]
+//
+// COMPAT23-V8-LABEL: define dso_local inreg { fp128, fp128 } @complex_long_double(
+// COMPAT23-V8-SAME: ptr noundef byval({ fp128, fp128 }) align 8 [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V8-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT: [[RETVAL:%.*]] = alloca { fp128, fp128 }, align 8
+// COMPAT23-V8-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[C_REAL:%.*]] = load fp128, ptr [[C_REALP]], align 8
+// COMPAT23-V8-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT: [[C_IMAG:%.*]] = load fp128, ptr [[C_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT: store fp128 [[C_REAL]], ptr [[RETVAL_REALP]], align 8
+// COMPAT23-V8-NEXT: store fp128 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// COMPAT23-V8-NEXT: [[TMP0:%.*]] = load { fp128, fp128 }, ptr [[RETVAL]], align 8
+// COMPAT23-V8-NEXT: ret { fp128, fp128 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local { fp128, fp128 } @complex_long_double(
+// COMPAT23-V9-SAME: ptr noundef align 16 dead_on_return [[C:%.*]]) #[[ATTR0]] {
+// COMPAT23-V9-NEXT: [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT: [[RETVAL:%.*]] = alloca { fp128, fp128 }, align 16
+// COMPAT23-V9-NEXT: [[C_INDIRECT_ADDR:%.*]] = alloca ptr, align 8
+// COMPAT23-V9-NEXT: store ptr [[C]], ptr [[C_INDIRECT_ADDR]], align 8
+// COMPAT23-V9-NEXT: [[C_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[C_REAL:%.*]] = load fp128, ptr [[C_REALP]], align 16
+// COMPAT23-V9-NEXT: [[C_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT: [[C_IMAG:%.*]] = load fp128, ptr [[C_IMAGP]], align 16
+// COMPAT23-V9-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT: store fp128 [[C_REAL]], ptr [[RETVAL_REALP]], align 16
+// COMPAT23-V9-NEXT: store fp128 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// COMPAT23-V9-NEXT: [[TMP0:%.*]] = load { fp128, fp128 }, ptr [[RETVAL]], align 16
+// COMPAT23-V9-NEXT: ret { fp128, fp128 } [[TMP0]]
+//
+_Complex long double complex_long_double(_Complex long double c) { return c; }
More information about the cfe-commits
mailing list