[clang] [Sparc][clang] make `_Complex` ABI GCC-compatible (PR #212340)

Folkert de Vries via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 1 14:45:14 PDT 2026


https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/212340

>From b60616cf6a51a109f256d4eb7bd50cb73ef0c358 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Mon, 27 Jul 2026 22:19:23 +0200
Subject: [PATCH 1/5] [Sparc][clang] make `_Complex` ABI GCC-compatible

---
 clang/docs/ReleaseNotes.md                   |  11 ++
 clang/include/clang/Basic/ABIVersions.def    |   6 ++
 clang/lib/CodeGen/Targets/Sparc.cpp          |  48 ++++++++-
 clang/test/CodeGen/Sparc/sparc-complex-abi.c | 104 +++++++++++++++++++
 4 files changed, 166 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/Sparc/sparc-complex-abi.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 61e8378b1704a..a47270ea5219c 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.
+
+- 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.
+
 ### AST Dumping Potentially Breaking Changes
 
 ### Clang Frontend Potentially Breaking Changes
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index b55e8dfa2bbc1..8d0e15ffc527a 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.
 ABI_VER_MAJOR(23)
 
 /// Conform to the underlying platform's C and C++ ABIs as closely as we can.
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 3fa4e84823d51..abc72468316f8 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -13,6 +13,13 @@
 using namespace clang;
 using namespace clang::CodeGen;
 
+/// Whether `_Complex` values with an integer element type are passed and
+/// returned the way GCC passes and returns them.
+static bool isComplexGnuABI(const ABIInfo &Info) {
+  return !Info.getContext().getLangOpts().isCompatibleWith(
+      LangOptions::ClangABI::Ver23);
+}
+
 //===----------------------------------------------------------------------===//
 // SPARC v8 ABI Implementation.
 // Based on the SPARC Compliance Definition version 2.4.1.
@@ -25,12 +32,30 @@ class SparcV8ABIInfo : public DefaultABIInfo {
   SparcV8ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
 
 private:
+  llvm::Type *getComplexIntCoerceType(QualType Ty) const;
   ABIArgInfo classifyReturnType(QualType RetTy) const;
   ABIArgInfo classifyArgumentType(QualType Ty) const;
   void computeInfo(CGFunctionInfo &FI) const override;
 };
 } // end anonymous namespace
 
+llvm::Type *SparcV8ABIInfo::getComplexIntCoerceType(QualType Ty) const {
+  if (!isComplexGnuABI(*this))
+    return nullptr;
+
+  const auto *CT = Ty->getAs<ComplexType>();
+  if (!CT || !CT->getElementType()->isIntegerType())
+    return nullptr;
+
+  // The default path already does the right thing for `long long _Complex`.
+  uint64_t Size = getContext().getTypeSize(Ty);
+  if (Size > 64)
+    return nullptr;
+
+  // Coerce to an integer to get the correct scalar-like behavior.
+  return llvm::IntegerType::get(getVMContext(), Size);
+}
+
 ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
   const auto *CT = Ty->getAs<ComplexType>();
   const auto *BT = Ty->getAs<BuiltinType>();
@@ -39,9 +64,13 @@ ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
   bool IsLongDouble = BT && BT->getKind() == BuiltinType::LongDouble;
 
   // long double _Complex is special in that it should be marked as inreg.
-  if (CT)
-    return IsLongDouble ? ABIArgInfo::getDirectInReg()
-                        : ABIArgInfo::getDirect();
+  if (CT) {
+    if (IsLongDouble)
+      return ABIArgInfo::getDirectInReg();
+    if (llvm::Type *CoerceTy = getComplexIntCoerceType(Ty))
+      return ABIArgInfo::getDirect(CoerceTy);
+    return ABIArgInfo::getDirect();
+  }
 
   if (IsLongDouble)
     return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
@@ -55,6 +84,10 @@ ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
       BT && BT->getKind() == BuiltinType::LongDouble)
     return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
 
+  // Complex integers go in registers.
+  if (llvm::Type *CoerceTy = getComplexIntCoerceType(Ty))
+    return ABIArgInfo::getDirect(CoerceTy);
+
   return DefaultABIInfo::classifyArgumentType(Ty);
 }
 
@@ -277,6 +310,15 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
       return ABIArgInfo::getExtend(Ty);
     }
 
+  // When being GCC-compatible, cast a complex integer to an integer type
+  // of the right size to get the correct scalar-like behavior.
+  if (isComplexGnuABI(*this))
+    if (const auto *CT = Ty->getAs<ComplexType>();
+        CT && Size < 64 && CT->getElementType()->isIntegerType()) {
+      RegOffset += 1;
+      return ABIArgInfo::getDirect(llvm::IntegerType::get(VMContext, Size));
+    }
+
   // Other non-aggregates go in registers.
   if (!isAggregateTypeForABI(Ty)) {
     RegOffset += Size / 64;
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..6f822134bdb0b
--- /dev/null
+++ b/clang/test/CodeGen/Sparc/sparc-complex-abi.c
@@ -0,0 +1,104 @@
+// 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.
+
+// Returns.
+//
+// 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.
+
+// COMPAT23-V8-LABEL: define{{.*}} { i8, i8 } @ret_complex_char(
+// V8-LABEL:          define{{.*}} i16 @ret_complex_char(
+// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_char(
+// V9-LABEL:          define{{.*}} i16 @ret_complex_char(
+_Complex char ret_complex_char(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} { i16, i16 } @ret_complex_short(
+// V8-LABEL:          define{{.*}} i32 @ret_complex_short(
+// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_short(
+// V9-LABEL:          define{{.*}} i32 @ret_complex_short(
+_Complex short ret_complex_short(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} { i32, i32 } @ret_complex_int(
+// V8-LABEL:          define{{.*}} i64 @ret_complex_int(
+// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_int(
+// V9-LABEL:          define{{.*}} i64 @ret_complex_int(
+_Complex int ret_complex_int(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} { i64, i64 } @ret_complex_long_long(
+// V8-LABEL:          define{{.*}} { i64, i64 } @ret_complex_long_long(
+// COMPAT23-V9-LABEL: define{{.*}} { i64, i64 } @ret_complex_long_long(
+// V9-LABEL:          define{{.*}} { i64, i64 } @ret_complex_long_long(
+_Complex long long ret_complex_long_long(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} { float, float } @ret_complex_float(
+// V8-LABEL:          define{{.*}} { float, float } @ret_complex_float(
+// COMPAT23-V9-LABEL: define{{.*}} inreg { float, float } @ret_complex_float(
+// V9-LABEL:          define{{.*}} inreg { float, float } @ret_complex_float(
+_Complex float ret_complex_float(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} { double, double } @ret_complex_double(
+// V8-LABEL:          define{{.*}} { double, double } @ret_complex_double(
+// COMPAT23-V9-LABEL: define{{.*}} { double, double } @ret_complex_double(
+// V9-LABEL:          define{{.*}} { double, double } @ret_complex_double(
+_Complex double ret_complex_double(void) { return 0; }
+
+// COMPAT23-V8-LABEL: define{{.*}} inreg { fp128, fp128 } @ret_complex_long_double(
+// V8-LABEL:          define{{.*}} inreg { fp128, fp128 } @ret_complex_long_double(
+// COMPAT23-V9-LABEL: define{{.*}} { fp128, fp128 } @ret_complex_long_double(
+// V9-LABEL:          define{{.*}} { fp128, fp128 } @ret_complex_long_double(
+_Complex long double ret_complex_long_double(void) { return 0; }
+
+// Arguments.
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_char(ptr noundef byval({ i8, i8 }) align 1 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_char(i16 noundef %c.coerce)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_char(i64 %c.coerce)
+// V9-LABEL:          define{{.*}} void @arg_complex_char(i16 noundef %c.coerce)
+void arg_complex_char(_Complex char c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_short(ptr noundef byval({ i16, i16 }) align 2 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_short(i32 noundef %c.coerce)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_short(i64 %c.coerce)
+// V9-LABEL:          define{{.*}} void @arg_complex_short(i32 noundef %c.coerce)
+void arg_complex_short(_Complex short c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_int(ptr noundef byval({ i32, i32 }) align 4 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
+// V9-LABEL:          define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
+void arg_complex_int(_Complex int c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_long_long(ptr noundef byval({ i64, i64 }) align 8 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_long_long(ptr noundef byval({ i64, i64 }) align 8 %c)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_long_long(i64 noundef %c.coerce0, i64 noundef %c.coerce1)
+// V9-LABEL:          define{{.*}} void @arg_complex_long_long(i64 noundef %c.coerce0, i64 noundef %c.coerce1)
+void arg_complex_long_long(_Complex long long c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_float(ptr noundef byval({ float, float }) align 4 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_float(ptr noundef byval({ float, float }) align 4 %c)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_float(float inreg noundef %c.coerce0, float inreg noundef %c.coerce1)
+// V9-LABEL:          define{{.*}} void @arg_complex_float(float inreg noundef %c.coerce0, float inreg noundef %c.coerce1)
+void arg_complex_float(_Complex float c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_double(ptr noundef byval({ double, double }) align 8 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_double(ptr noundef byval({ double, double }) align 8 %c)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_double(double noundef %c.coerce0, double noundef %c.coerce1)
+// V9-LABEL:          define{{.*}} void @arg_complex_double(double noundef %c.coerce0, double noundef %c.coerce1)
+void arg_complex_double(_Complex double c) {}
+
+// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_long_double(ptr noundef byval({ fp128, fp128 }) align 8 %c)
+// V8-LABEL:          define{{.*}} void @arg_complex_long_double(ptr noundef byval({ fp128, fp128 }) align 8 %c)
+// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_long_double(ptr noundef align 16 dead_on_return %c)
+// V9-LABEL:          define{{.*}} void @arg_complex_long_double(ptr noundef align 16 dead_on_return %c)
+void arg_complex_long_double(_Complex long double c) {}

>From f8a3c8ffec62cc9eb19e40ecfa47d6629ef8c1c0 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 1 Aug 2026 22:31:04 +0200
Subject: [PATCH 2/5] update test

---
 clang/test/CodeGen/Sparc/sparc-complex-abi.c | 641 ++++++++++++++++---
 1 file changed, 561 insertions(+), 80 deletions(-)

diff --git a/clang/test/CodeGen/Sparc/sparc-complex-abi.c b/clang/test/CodeGen/Sparc/sparc-complex-abi.c
index 6f822134bdb0b..55605032be17b 100644
--- a/clang/test/CodeGen/Sparc/sparc-complex-abi.c
+++ b/clang/test/CodeGen/Sparc/sparc-complex-abi.c
@@ -1,3 +1,4 @@
+// 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 \
@@ -10,95 +11,575 @@
 
 // Test how SPARC passes and returns `_Complex` values.
 
-// Returns.
-//
 // 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.
 
-// COMPAT23-V8-LABEL: define{{.*}} { i8, i8 } @ret_complex_char(
-// V8-LABEL:          define{{.*}} i16 @ret_complex_char(
-// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_char(
-// V9-LABEL:          define{{.*}} i16 @ret_complex_char(
-_Complex char ret_complex_char(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} { i16, i16 } @ret_complex_short(
-// V8-LABEL:          define{{.*}} i32 @ret_complex_short(
-// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_short(
-// V9-LABEL:          define{{.*}} i32 @ret_complex_short(
-_Complex short ret_complex_short(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} { i32, i32 } @ret_complex_int(
-// V8-LABEL:          define{{.*}} i64 @ret_complex_int(
-// COMPAT23-V9-LABEL: define{{.*}} i64 @ret_complex_int(
-// V9-LABEL:          define{{.*}} i64 @ret_complex_int(
-_Complex int ret_complex_int(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} { i64, i64 } @ret_complex_long_long(
-// V8-LABEL:          define{{.*}} { i64, i64 } @ret_complex_long_long(
-// COMPAT23-V9-LABEL: define{{.*}} { i64, i64 } @ret_complex_long_long(
-// V9-LABEL:          define{{.*}} { i64, i64 } @ret_complex_long_long(
-_Complex long long ret_complex_long_long(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} { float, float } @ret_complex_float(
-// V8-LABEL:          define{{.*}} { float, float } @ret_complex_float(
-// COMPAT23-V9-LABEL: define{{.*}} inreg { float, float } @ret_complex_float(
-// V9-LABEL:          define{{.*}} inreg { float, float } @ret_complex_float(
-_Complex float ret_complex_float(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} { double, double } @ret_complex_double(
-// V8-LABEL:          define{{.*}} { double, double } @ret_complex_double(
-// COMPAT23-V9-LABEL: define{{.*}} { double, double } @ret_complex_double(
-// V9-LABEL:          define{{.*}} { double, double } @ret_complex_double(
-_Complex double ret_complex_double(void) { return 0; }
-
-// COMPAT23-V8-LABEL: define{{.*}} inreg { fp128, fp128 } @ret_complex_long_double(
-// V8-LABEL:          define{{.*}} inreg { fp128, fp128 } @ret_complex_long_double(
-// COMPAT23-V9-LABEL: define{{.*}} { fp128, fp128 } @ret_complex_long_double(
-// V9-LABEL:          define{{.*}} { fp128, fp128 } @ret_complex_long_double(
-_Complex long double ret_complex_long_double(void) { return 0; }
-
-// Arguments.
+// 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_char(ptr noundef byval({ i8, i8 }) align 1 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_char(i16 noundef %c.coerce)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_char(i64 %c.coerce)
-// V9-LABEL:          define{{.*}} void @arg_complex_char(i16 noundef %c.coerce)
-void arg_complex_char(_Complex char 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_short(ptr noundef byval({ i16, i16 }) align 2 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_short(i32 noundef %c.coerce)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_short(i64 %c.coerce)
-// V9-LABEL:          define{{.*}} void @arg_complex_short(i32 noundef %c.coerce)
-void arg_complex_short(_Complex short 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_int(ptr noundef byval({ i32, i32 }) align 4 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
-// V9-LABEL:          define{{.*}} void @arg_complex_int(i64 noundef %c.coerce)
-void arg_complex_int(_Complex int 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_long_long(ptr noundef byval({ i64, i64 }) align 8 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_long_long(ptr noundef byval({ i64, i64 }) align 8 %c)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_long_long(i64 noundef %c.coerce0, i64 noundef %c.coerce1)
-// V9-LABEL:          define{{.*}} void @arg_complex_long_long(i64 noundef %c.coerce0, i64 noundef %c.coerce1)
-void arg_complex_long_long(_Complex long long 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_float(ptr noundef byval({ float, float }) align 4 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_float(ptr noundef byval({ float, float }) align 4 %c)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_float(float inreg noundef %c.coerce0, float inreg noundef %c.coerce1)
-// V9-LABEL:          define{{.*}} void @arg_complex_float(float inreg noundef %c.coerce0, float inreg noundef %c.coerce1)
-void arg_complex_float(_Complex float 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_double(ptr noundef byval({ double, double }) align 8 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_double(ptr noundef byval({ double, double }) align 8 %c)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_double(double noundef %c.coerce0, double noundef %c.coerce1)
-// V9-LABEL:          define{{.*}} void @arg_complex_double(double noundef %c.coerce0, double noundef %c.coerce1)
-void arg_complex_double(_Complex double 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; }
 
-// COMPAT23-V8-LABEL: define{{.*}} void @arg_complex_long_double(ptr noundef byval({ fp128, fp128 }) align 8 %c)
-// V8-LABEL:          define{{.*}} void @arg_complex_long_double(ptr noundef byval({ fp128, fp128 }) align 8 %c)
-// COMPAT23-V9-LABEL: define{{.*}} void @arg_complex_long_double(ptr noundef align 16 dead_on_return %c)
-// V9-LABEL:          define{{.*}} void @arg_complex_long_double(ptr noundef align 16 dead_on_return %c)
-void arg_complex_long_double(_Complex long double 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; }

>From 16581565671b23dee2433c37745b3f98d8bf95fe Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 1 Aug 2026 23:29:29 +0200
Subject: [PATCH 3/5] make isComplexGnuABI a member variable

---
 clang/lib/CodeGen/Targets/Sparc.cpp | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index abc72468316f8..2ea5c1a4ea2d5 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -13,13 +13,6 @@
 using namespace clang;
 using namespace clang::CodeGen;
 
-/// Whether `_Complex` values with an integer element type are passed and
-/// returned the way GCC passes and returns them.
-static bool isComplexGnuABI(const ABIInfo &Info) {
-  return !Info.getContext().getLangOpts().isCompatibleWith(
-      LangOptions::ClangABI::Ver23);
-}
-
 //===----------------------------------------------------------------------===//
 // SPARC v8 ABI Implementation.
 // Based on the SPARC Compliance Definition version 2.4.1.
@@ -29,9 +22,15 @@ static bool isComplexGnuABI(const ABIInfo &Info) {
 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;
+
   llvm::Type *getComplexIntCoerceType(QualType Ty) const;
   ABIArgInfo classifyReturnType(QualType RetTy) const;
   ABIArgInfo classifyArgumentType(QualType Ty) const;
@@ -40,7 +39,7 @@ class SparcV8ABIInfo : public DefaultABIInfo {
 } // end anonymous namespace
 
 llvm::Type *SparcV8ABIInfo::getComplexIntCoerceType(QualType Ty) const {
-  if (!isComplexGnuABI(*this))
+  if (!IsComplexGnuABI)
     return nullptr;
 
   const auto *CT = Ty->getAs<ComplexType>();
@@ -156,9 +155,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;
@@ -312,7 +317,7 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
 
   // When being GCC-compatible, cast a complex integer to an integer type
   // of the right size to get the correct scalar-like behavior.
-  if (isComplexGnuABI(*this))
+  if (IsComplexGnuABI)
     if (const auto *CT = Ty->getAs<ComplexType>();
         CT && Size < 64 && CT->getElementType()->isIntegerType()) {
       RegOffset += 1;

>From f4c19de19fe9c2b731dee69285f1370279b95dfd Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 1 Aug 2026 23:29:29 +0200
Subject: [PATCH 4/5] use element type size

---
 clang/lib/CodeGen/Targets/Sparc.cpp | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 2ea5c1a4ea2d5..f7c7a15e4f966 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -47,12 +47,12 @@ llvm::Type *SparcV8ABIInfo::getComplexIntCoerceType(QualType Ty) const {
     return nullptr;
 
   // The default path already does the right thing for `long long _Complex`.
-  uint64_t Size = getContext().getTypeSize(Ty);
-  if (Size > 64)
+  uint64_t ElementTypeSize = getContext().getTypeSize(CT->getElementType());
+  if (ElementTypeSize > 32)
     return nullptr;
 
   // Coerce to an integer to get the correct scalar-like behavior.
-  return llvm::IntegerType::get(getVMContext(), Size);
+  return llvm::IntegerType::get(getVMContext(), 2 * ElementTypeSize);
 }
 
 ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
@@ -317,12 +317,17 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
 
   // When being GCC-compatible, cast a complex integer to an integer type
   // of the right size to get the correct scalar-like behavior.
-  if (IsComplexGnuABI)
-    if (const auto *CT = Ty->getAs<ComplexType>();
-        CT && Size < 64 && CT->getElementType()->isIntegerType()) {
-      RegOffset += 1;
-      return ABIArgInfo::getDirect(llvm::IntegerType::get(VMContext, Size));
+  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));
+      }
     }
+  }
 
   // Other non-aggregates go in registers.
   if (!isAggregateTypeForABI(Ty)) {

>From 27c742a9a3b0c75fff444755601b382fb73adf4a Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 1 Aug 2026 23:25:04 +0200
Subject: [PATCH 5/5] `getComplexIntCoerceType` -> `classifyComplexType`

---
 clang/lib/CodeGen/Targets/Sparc.cpp | 66 ++++++++++++++---------------
 1 file changed, 32 insertions(+), 34 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index f7c7a15e4f966..32a71e4811c7d 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -31,47 +31,46 @@ class SparcV8ABIInfo : public DefaultABIInfo {
   /// Whether how `_Complex` values are passed and returned is GCC-compatible.
   bool IsComplexGnuABI;
 
-  llvm::Type *getComplexIntCoerceType(QualType Ty) const;
+  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
 
-llvm::Type *SparcV8ABIInfo::getComplexIntCoerceType(QualType Ty) const {
-  if (!IsComplexGnuABI)
-    return nullptr;
+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));
+    }
+  }
 
-  const auto *CT = Ty->getAs<ComplexType>();
-  if (!CT || !CT->getElementType()->isIntegerType())
-    return nullptr;
+  // Any other complex value is passed indirectly, but returned in registers.
+  if (!IsRet)
+    return getNaturalAlignIndirect(QualType(CT, 0),
+                                   getDataLayout().getAllocaAddrSpace());
 
-  // The default path already does the right thing for `long long _Complex`.
-  uint64_t ElementTypeSize = getContext().getTypeSize(CT->getElementType());
-  if (ElementTypeSize > 32)
-    return nullptr;
+  // 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();
 
-  // Coerce to an integer to get the correct scalar-like behavior.
-  return llvm::IntegerType::get(getVMContext(), 2 * ElementTypeSize);
+  return ABIArgInfo::getDirect();
 }
 
 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;
-
-  // long double _Complex is special in that it should be marked as inreg.
-  if (CT) {
-    if (IsLongDouble)
-      return ABIArgInfo::getDirectInReg();
-    if (llvm::Type *CoerceTy = getComplexIntCoerceType(Ty))
-      return ABIArgInfo::getDirect(CoerceTy);
-    return ABIArgInfo::getDirect();
-  }
+  if (const auto *CT = Ty->getAs<ComplexType>())
+    return classifyComplexType(CT, /*IsRet=*/true);
 
-  if (IsLongDouble)
+  if (const auto *BT = Ty->getAs<BuiltinType>();
+      BT && BT->getKind() == BuiltinType::LongDouble)
     return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
                                    /*ByVal=*/false);
 
@@ -79,13 +78,12 @@ ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
 }
 
 ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
-  if (const auto *BT = Ty->getAs<BuiltinType>();
-      BT && BT->getKind() == BuiltinType::LongDouble)
-    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+  if (const auto *CT = Ty->getAs<ComplexType>())
+    return classifyComplexType(CT, /*IsRet=*/false);
 
-  // Complex integers go in registers.
-  if (llvm::Type *CoerceTy = getComplexIntCoerceType(Ty))
-    return ABIArgInfo::getDirect(CoerceTy);
+  const auto *BT = Ty->getAs<BuiltinType>();
+  if (BT && BT->getKind() == BuiltinType::LongDouble)
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
 
   return DefaultABIInfo::classifyArgumentType(Ty);
 }



More information about the cfe-commits mailing list