[clang] [PowerPC] make `_Complex` ABI GCC-compatible (PR #208917)
Folkert de Vries via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 11 08:09:20 PDT 2026
https://github.com/folkertdev created https://github.com/llvm/llvm-project/pull/208917
Replaces https://github.com/llvm/llvm-project/pull/77732 and truly fixes https://github.com/llvm/llvm-project/issues/56023.
>From 816fead49d979c12def085719f70fea79b940e66 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 13:27:46 +0200
Subject: [PATCH 1/7] inline `ABIArgInfo::classifyArgumentType`
---
clang/lib/CodeGen/Targets/PPC.cpp | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index ab069bfbd1b51..19da37da1a333 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -370,6 +370,7 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
+ ABIArgInfo classifyArgumentType(QualType Ty) const;
void computeInfo(CGFunctionInfo &FI) const override {
if (!getCXXABI().classifyReturnType(FI))
@@ -426,6 +427,36 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty) const {
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ if (isAggregateTypeForABI(Ty)) {
+ // Records with non-trivial destructors/copy-constructors should not be
+ // passed by value.
+ if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
+ return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
+ RAA == CGCXXABI::RAA_DirectInMemory);
+
+ return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+ }
+
+ // Treat an enum type as its underlying type.
+ if (const auto *ED = Ty->getAsEnumDecl())
+ Ty = ED->getIntegerType();
+
+ ASTContext &Context = getContext();
+ if (const auto *EIT = Ty->getAs<BitIntType>())
+ if (EIT->getNumBits() >
+ Context.getTypeSize(Context.getTargetInfo().hasInt128Type()
+ ? Context.Int128Ty
+ : Context.LongLongTy))
+ return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+
+ return (isPromotableIntegerTypeForABI(Ty)
+ ? ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty))
+ : ABIArgInfo::getDirect());
+}
+
ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
uint64_t Size;
>From 70f324bbb1d32ab71fe646591be990ed41388caf Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 13:29:53 +0200
Subject: [PATCH 2/7] add `isComplexGnuABI`
---
clang/lib/CodeGen/Targets/PPC.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 19da37da1a333..bc031be5612a5 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -361,6 +361,12 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
bool IsSoftFloatABI;
bool IsRetSmallStructInRegABI;
+ bool isComplexGnuABI() const {
+ return !getTarget().getTriple().isOSDarwin() &&
+ !getContext().getLangOpts().isCompatibleWith(
+ LangOptions::ClangABI::Ver22);
+ }
+
CharUnits getParamTypeAlignment(QualType Ty) const;
public:
>From d1f172171568c0f8d7cbc461020169f0294330f3 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 13:36:43 +0200
Subject: [PATCH 3/7] add `ArgGPRsLeft` parameter to `classifyArgumentType`
---
clang/lib/CodeGen/Targets/PPC.cpp | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index bc031be5612a5..ded9ef9a21328 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -361,6 +361,10 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
bool IsSoftFloatABI;
bool IsRetSmallStructInRegABI;
+ // Number of GPRs (r3..=r10) available for passing arguments, and their width.
+ static const int NumArgGPRs = 8;
+ static const unsigned GPRBits = 32;
+
bool isComplexGnuABI() const {
return !getTarget().getTriple().isOSDarwin() &&
!getContext().getLangOpts().isCompatibleWith(
@@ -376,13 +380,15 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
- ABIArgInfo classifyArgumentType(QualType Ty) const;
+ ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const;
void computeInfo(CGFunctionInfo &FI) const override {
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+
+ int ArgGPRsLeft = NumArgGPRs;
for (auto &I : FI.arguments())
- I.info = classifyArgumentType(I.type);
+ I.info = classifyArgumentType(I.type, ArgGPRsLeft);
}
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
@@ -433,7 +439,9 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}
-ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty) const {
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
+ int &ArgGPRsLeft) const {
+ assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
Ty = useFirstFieldIfTransparentUnion(Ty);
if (isAggregateTypeForABI(Ty)) {
@@ -500,8 +508,10 @@ RValue PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
TI.Align = getParamTypeAlignment(Ty);
CharUnits SlotSize = CharUnits::fromQuantity(4);
+ int ArgGPRsLeft = NumArgGPRs;
return emitVoidPtrVAArg(CGF, VAList, Ty,
- classifyArgumentType(Ty).isIndirect(), TI, SlotSize,
+ classifyArgumentType(Ty, ArgGPRsLeft).isIndirect(),
+ TI, SlotSize,
/*AllowHigherAlign=*/true, Slot);
}
>From 04311c791fd058d22b78ff16bb1d1d466dc32957 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 14:01:32 +0200
Subject: [PATCH 4/7] add GPR budget logic
---
clang/lib/CodeGen/Targets/PPC.cpp | 50 ++++++++++++++++++-------------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index ded9ef9a21328..824e0d4864a2e 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -444,31 +444,39 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
Ty = useFirstFieldIfTransparentUnion(Ty);
- if (isAggregateTypeForABI(Ty)) {
- // Records with non-trivial destructors/copy-constructors should not be
- // passed by value.
- if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
- return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
- RAA == CGCXXABI::RAA_DirectInMemory);
+ bool IsComplex = Ty->isAnyComplexType() && isComplexGnuABI();
- return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
- }
+ // Use the default implementation when this argument is not relevant for GPR
+ // budget:
+ //
+ // - floating-point types are passed in FPRs
+ // - when GPRs are already exhausted
+ //
+ // Complex types (when GNU compatible) always need custom handling.
+ if (!IsComplex && (!ArgGPRsLeft || (Ty->isFloatingType() && !IsSoftFloatABI)))
+ return DefaultABIInfo::classifyArgumentType(Ty);
- // Treat an enum type as its underlying type.
- if (const auto *ED = Ty->getAsEnumDecl())
- Ty = ED->getIntegerType();
+ uint64_t TypeSize = getContext().getTypeSize(Ty);
+ uint64_t RegsNeeded = (TypeSize + GPRBits - 1) / GPRBits;
- ASTContext &Context = getContext();
- if (const auto *EIT = Ty->getAs<BitIntType>())
- if (EIT->getNumBits() >
- Context.getTypeSize(Context.getTargetInfo().hasInt128Type()
- ? Context.Int128Ty
- : Context.LongLongTy))
- return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+ if (IsComplex || !isAggregateTypeForABI(Ty)) {
+ // Complex values and scalars are passed in GPRs.
- return (isPromotableIntegerTypeForABI(Ty)
- ? ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty))
- : ABIArgInfo::getDirect());
+ // An 8-byte value (e.g. _Complex float, _Complex int, i64, soft-float
+ // double) must start in an even-numbered GPR, so skip an odd register to
+ // keep the pair aligned.
+ if (TypeSize == 2 * GPRBits && ArgGPRsLeft % 2 == 1)
+ ArgGPRsLeft -= 1;
+
+ if (RegsNeeded <= (uint64_t)ArgGPRsLeft) {
+ ArgGPRsLeft -= RegsNeeded;
+ }
+ } else {
+ // Other aggregates are passed indirectly, and consume one GPR.
+ ArgGPRsLeft -= 1;
+ }
+
+ return DefaultABIInfo::classifyArgumentType(Ty);
}
ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
>From e67311b895949c9b811c643a2b53dd3212fb8ef1 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 15:10:03 +0200
Subject: [PATCH 5/7] add `classifyComplexType`
---
clang/lib/CodeGen/Targets/PPC.cpp | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 824e0d4864a2e..2c5fa5e5ebb5a 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -372,6 +372,7 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
}
CharUnits getParamTypeAlignment(QualType Ty) const;
+ ABIArgInfo classifyComplexType(QualType Ty) const;
public:
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
@@ -439,6 +440,32 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyComplexType(QualType Ty) const {
+ uint64_t Size = getContext().getTypeSize(Ty);
+ llvm::LLVMContext &VMC = getVMContext();
+ llvm::Type *I32 = llvm::Type::getInt32Ty(VMC);
+ QualType ElemTy = Ty->castAs<ComplexType>()->getElementType();
+
+ // Work around https://github.com/llvm/llvm-project/issues/44482.
+ // This is a bug where the two halves of a ppc_fp128 are swapped.
+ // Using i128 for the ABI instead circumvents this issue.
+ if (CGT.ConvertType(ElemTy)->isPPC_FP128Ty())
+ return ABIArgInfo::getDirect(
+ llvm::ArrayType::get(llvm::Type::getInt128Ty(VMC), 2));
+
+ // Coerce to an integer for _Complex char and _Complex short.
+ if (Size <= GPRBits)
+ return ABIArgInfo::getDirect(llvm::IntegerType::get(VMC, Size));
+
+ // Coerce to a vector <N x i32> for _Complex float and _Complex int.
+ if (Size == 2 * GPRBits)
+ return ABIArgInfo::getDirect(llvm::FixedVectorType::get(I32, 2));
+
+ // Coerce to an array [N x i32] for _Complex double and similar.
+ // Using i32 gives the correct 4-byte register alignment.
+ return ABIArgInfo::getDirect(llvm::ArrayType::get(I32, Size / GPRBits));
+}
+
ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
int &ArgGPRsLeft) const {
assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
>From 9af49c31ff1c1368c1eae18ada6f68e78598cdd0 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 15:18:33 +0200
Subject: [PATCH 6/7] hook up new complex abi logic
---
clang/lib/CodeGen/Targets/PPC.cpp | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 2c5fa5e5ebb5a..fc07e57656d09 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -444,6 +444,8 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyComplexType(QualType Ty) const {
uint64_t Size = getContext().getTypeSize(Ty);
llvm::LLVMContext &VMC = getVMContext();
llvm::Type *I32 = llvm::Type::getInt32Ty(VMC);
+
+ assert(Ty->isAnyComplexType() && "not a complex type");
QualType ElemTy = Ty->castAs<ComplexType>()->getElementType();
// Work around https://github.com/llvm/llvm-project/issues/44482.
@@ -458,11 +460,12 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyComplexType(QualType Ty) const {
return ABIArgInfo::getDirect(llvm::IntegerType::get(VMC, Size));
// Coerce to a vector <N x i32> for _Complex float and _Complex int.
+ // A vector gives this the correct 8-byte register alignment.
if (Size == 2 * GPRBits)
return ABIArgInfo::getDirect(llvm::FixedVectorType::get(I32, 2));
// Coerce to an array [N x i32] for _Complex double and similar.
- // Using i32 gives the correct 4-byte register alignment.
+ // An array of i32 gives the correct 4-byte register alignment.
return ABIArgInfo::getDirect(llvm::ArrayType::get(I32, Size / GPRBits));
}
@@ -496,7 +499,28 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
ArgGPRsLeft -= 1;
if (RegsNeeded <= (uint64_t)ArgGPRsLeft) {
+ // All fits.
ArgGPRsLeft -= RegsNeeded;
+
+ // _Complex needs a type coercion to be passed correctly.
+ if (IsComplex)
+ return classifyComplexType(Ty);
+ } else if (IsComplex) {
+ // Never split a Complex value across GPRs and the stack. When a Complex
+ // value does not fit in the remaining GPRs, it is passed via the stack
+ // and the remaining GPRs are considered consumed, so any further
+ // arguments will be passed via the stack as well.
+
+ // _Complex needs a type coercion to be passed correctly.
+ llvm::Type *CoerceTy = classifyComplexType(Ty).getCoerceToType();
+
+ // Soak up the remaining GPRs with a padding argument.
+ llvm::Type *I32 = llvm::Type::getInt32Ty(getVMContext());
+ llvm::Type *Padding =
+ ArgGPRsLeft > 0 ? llvm::ArrayType::get(I32, ArgGPRsLeft) : nullptr;
+
+ ArgGPRsLeft = 0;
+ return ABIArgInfo::getDirect(CoerceTy, /*Offset=*/0, Padding);
}
} else {
// Other aggregates are passed indirectly, and consume one GPR.
@@ -509,6 +533,9 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
uint64_t Size;
+ if (RetTy->isAnyComplexType() && isComplexGnuABI())
+ return classifyComplexType(RetTy);
+
// -msvr4-struct-return puts small aggregates in GPR3 and GPR4.
if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI &&
(Size = getContext().getTypeSize(RetTy)) <= 64) {
>From 8b25c94d7b10e12eb5da1b47dc6718d801cdb067 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Sat, 11 Jul 2026 16:57:51 +0200
Subject: [PATCH 7/7] fix test
---
.../test/CodeGen/PowerPC/powerpc-c99complex.c | 92 +++++++++++++++----
1 file changed, 72 insertions(+), 20 deletions(-)
diff --git a/clang/test/CodeGen/PowerPC/powerpc-c99complex.c b/clang/test/CodeGen/PowerPC/powerpc-c99complex.c
index 8911257741c33..b6b6832bce3df 100644
--- a/clang/test/CodeGen/PowerPC/powerpc-c99complex.c
+++ b/clang/test/CodeGen/PowerPC/powerpc-c99complex.c
@@ -1,19 +1,37 @@
-// RUN: %clang_cc1 -triple powerpc64-unknown-aix -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-NOLDBL128
-// RUN: %clang_cc1 -triple powerpc-unknown-aix -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-NOLDBL128
-// RUN: %clang_cc1 -triple powerpc64-unknown-linux -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-LDBL128
-// RUN: %clang_cc1 -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-LDBL128
-// RUN: %clang_cc1 -triple powerpc-unknown-linux -emit-llvm %s -o - | FileCheck %s --check-prefix=PPC32LNX
+// RUN: %clang_cc1 -triple powerpc64-unknown-aix -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK-NOLDBL128,CHECK
+// RUN: %clang_cc1 -triple powerpc-unknown-aix -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK-NOLDBL128,CHECK
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK-LDBL128,CHECK
+// RUN: %clang_cc1 -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK-LDBL128,CHECK
+// RUN: %clang_cc1 -triple powerpc-unknown-linux -fclang-abi-compat=22 -emit-llvm %s -o - | FileCheck %s --check-prefix=PPC32LNX-CLANG22
+// RUN: %clang_cc1 -triple powerpc-unknown-linux -fclang-abi-compat=23 -emit-llvm %s -o - | FileCheck %s --check-prefix=PPC32LNX-CLANG23
_Complex float foo1(_Complex float x) {
return x;
// CHECK-LABEL: define{{.*}} { float, float } @foo1(float noundef %x.{{.*}}, float noundef %x.{{.*}}) #0 {
// CHECK: ret { float, float }
-// PPC32LNX-LABEL: define{{.*}} void @foo1(ptr dead_on_unwind noalias writable sret({ float, float }) align 4 %agg.result, ptr noundef byval({ float, float }) align 4 %x) #0 {
-// PPC32LNX: [[RETREAL:%.*]] = getelementptr inbounds nuw { float, float }, ptr %agg.result, i32 0, i32 0
-// PPC32LNX-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { float, float }, ptr %agg.result, i32 0, i32 1
-// PPC32LNX-NEXT: store float %{{.*}}, ptr [[RETREAL]], align 4
-// PPC32LNX-NEXT: store float %{{.*}}, ptr [[RETIMAG]], align 4
+// PPC32LNX-CLANG22-LABEL: define{{.*}} void @foo1(ptr dead_on_unwind noalias writable sret({ float, float }) align 4 %agg.result, ptr noundef byval({ float, float }) align 4 %x) #0 {
+// PPC32LNX-CLANG22: [[RETREAL:%.*]] = getelementptr inbounds nuw { float, float }, ptr %agg.result, i32 0, i32 0
+// PPC32LNX-CLANG22-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { float, float }, ptr %agg.result, i32 0, i32 1
+// PPC32LNX-CLANG22-NEXT: store float %{{.*}}, ptr [[RETREAL]], align 4
+// PPC32LNX-CLANG22-NEXT: store float %{{.*}}, ptr [[RETIMAG]], align 4
+
+// PPC32LNX-CLANG23-LABEL: define dso_local <2 x i32> @foo1(
+// PPC32LNX-CLANG23-SAME: <2 x i32> noundef [[X_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// PPC32LNX-CLANG23-NEXT: [[ENTRY:.*:]]
+// PPC32LNX-CLANG23-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// PPC32LNX-CLANG23-NEXT: [[X:%.*]] = alloca { float, float }, align 4
+// PPC32LNX-CLANG23-NEXT: store <2 x i32> [[X_COERCE]], ptr [[X]], align 4
+// PPC32LNX-CLANG23-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// PPC32LNX-CLANG23-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: store float [[X_REAL]], ptr [[RETVAL_REALP]], align 4
+// PPC32LNX-CLANG23-NEXT: store float [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// PPC32LNX-CLANG23-NEXT: [[TMP0:%.*]] = load <2 x i32>, ptr [[RETVAL]], align 4
+// PPC32LNX-CLANG23-NEXT: ret <2 x i32> [[TMP0]]
}
_Complex double foo2(_Complex double x) {
@@ -21,11 +39,28 @@ _Complex double foo2(_Complex double x) {
// CHECK-LABEL: define{{.*}} { double, double } @foo2(double noundef %x.{{.*}}, double noundef %x.{{.*}}) #0 {
// CHECK: ret { double, double }
-// PPC32LNX-LABEL: define{{.*}} void @foo2(ptr dead_on_unwind noalias writable sret({ double, double }) align 8 %agg.result, ptr noundef byval({ double, double }) align 8 %x) #0 {
-// PPC32LNX: [[RETREAL:%.*]] = getelementptr inbounds nuw { double, double }, ptr %agg.result, i32 0, i32 0
-// PPC32LNX-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { double, double }, ptr %agg.result, i32 0, i32 1
-// PPC32LNX-NEXT: store double %{{.*}}, ptr [[RETREAL]], align 8
-// PPC32LNX-NEXT: store double %{{.*}}, ptr [[RETIMAG]], align 8
+// PPC32LNX-CLANG22-LABEL: define{{.*}} void @foo2(ptr dead_on_unwind noalias writable sret({ double, double }) align 8 %agg.result, ptr noundef byval({ double, double }) align 8 %x) #0 {
+// PPC32LNX-CLANG22: [[RETREAL:%.*]] = getelementptr inbounds nuw { double, double }, ptr %agg.result, i32 0, i32 0
+// PPC32LNX-CLANG22-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { double, double }, ptr %agg.result, i32 0, i32 1
+// PPC32LNX-CLANG22-NEXT: store double %{{.*}}, ptr [[RETREAL]], align 8
+// PPC32LNX-CLANG22-NEXT: store double %{{.*}}, ptr [[RETIMAG]], align 8
+
+// PPC32LNX-CLANG23-LABEL: define dso_local [4 x i32] @foo2(
+// PPC32LNX-CLANG23-SAME: [4 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// PPC32LNX-CLANG23-NEXT: [[ENTRY:.*:]]
+// PPC32LNX-CLANG23-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// PPC32LNX-CLANG23-NEXT: [[X:%.*]] = alloca { double, double }, align 8
+// PPC32LNX-CLANG23-NEXT: store [4 x i32] [[X_COERCE]], ptr [[X]], align 8
+// PPC32LNX-CLANG23-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// PPC32LNX-CLANG23-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: store double [[X_REAL]], ptr [[RETVAL_REALP]], align 8
+// PPC32LNX-CLANG23-NEXT: store double [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// PPC32LNX-CLANG23-NEXT: [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// PPC32LNX-CLANG23-NEXT: ret [4 x i32] [[TMP0]]
}
_Complex long double foo3(_Complex long double x) {
@@ -36,9 +71,26 @@ _Complex long double foo3(_Complex long double x) {
// CHECK-LDBL128-LABEL: define{{.*}} { ppc_fp128, ppc_fp128 } @foo3(ppc_fp128 noundef %x.{{.*}}, ppc_fp128 noundef %x.{{.*}}) #0 {
// CHECK-LDBL128: ret { ppc_fp128, ppc_fp128 }
-// PPC32LNX-LABEL: define{{.*}} void @foo3(ptr dead_on_unwind noalias writable sret({ ppc_fp128, ppc_fp128 }) align 16 %agg.result, ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 %x) #0 {
-// PPC32LNX: [[RETREAL:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr %agg.result, i32 0, i32 0
-// PPC32LNX-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr %agg.result, i32 0, i32 1
-// PPC32LNX-NEXT: store ppc_fp128 %{{.*}}, ptr [[RETREAL]], align 16
-// PPC32LNX-NEXT: store ppc_fp128 %{{.*}}, ptr [[RETIMAG]], align 16
+// PPC32LNX-CLANG22-LABEL: define{{.*}} void @foo3(ptr dead_on_unwind noalias writable sret({ ppc_fp128, ppc_fp128 }) align 16 %agg.result, ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 %x) #0 {
+// PPC32LNX-CLANG22: [[RETREAL:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr %agg.result, i32 0, i32 0
+// PPC32LNX-CLANG22-NEXT: [[RETIMAG:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr %agg.result, i32 0, i32 1
+// PPC32LNX-CLANG22-NEXT: store ppc_fp128 %{{.*}}, ptr [[RETREAL]], align 16
+// PPC32LNX-CLANG22-NEXT: store ppc_fp128 %{{.*}}, ptr [[RETIMAG]], align 16
+
+// PPC32LNX-CLANG23-LABEL: define dso_local [2 x i128] @foo3(
+// PPC32LNX-CLANG23-SAME: [2 x i128] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// PPC32LNX-CLANG23-NEXT: [[ENTRY:.*:]]
+// PPC32LNX-CLANG23-NEXT: [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// PPC32LNX-CLANG23-NEXT: [[X:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16
+// PPC32LNX-CLANG23-NEXT: store [2 x i128] [[X_COERCE]], ptr [[X]], align 16
+// PPC32LNX-CLANG23-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16
+// PPC32LNX-CLANG23-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 0
+// PPC32LNX-CLANG23-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 1
+// PPC32LNX-CLANG23-NEXT: store ppc_fp128 [[X_REAL]], ptr [[RETVAL_REALP]], align 16
+// PPC32LNX-CLANG23-NEXT: store ppc_fp128 [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 16
+// PPC32LNX-CLANG23-NEXT: [[TMP0:%.*]] = load [2 x i128], ptr [[RETVAL]], align 16
+// PPC32LNX-CLANG23-NEXT: ret [2 x i128] [[TMP0]]
}
More information about the cfe-commits
mailing list