[PowerPC] make _Complex ABI GCC-compatible - #208917
Conversation
8b25c94 to
7f395d8
Compare
|
@llvm/pr-subscribers-backend-powerpc @llvm/pr-subscribers-clang-codegen Author: Folkert de Vries (folkertdev) ChangesReplaces #77732 and truly fixes #56023. The design, as per #77732 (comment), is that So, the challenge is of course how to test and review all this. I have a collection of input file that I used for testing empirically versus GCC: https://github.com/folkertdev/powerpc-complex-abi-validation I'd happily just add (a subset of) those and run the And the GCC logic is non-trivial: some types must be converted to integers, vectors or arrays to get the right behavior. There is register alignment for some types. Future work is to make Patch is 60.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208917.diff 5 Files Affected:
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index ab069bfbd1b51..fc07e57656d09 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -361,7 +361,18 @@ 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(
+ LangOptions::ClangABI::Ver22);
+ }
+
CharUnits getParamTypeAlignment(QualType Ty) const;
+ ABIArgInfo classifyComplexType(QualType Ty) const;
public:
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
@@ -370,12 +381,15 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
+ ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const;
void computeInfo(CGFunctionInfo &FI) const override {
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+
+ int ArgGPRsLeft = 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,
@@ -426,9 +440,102 @@ 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);
+
+ assert(Ty->isAnyComplexType() && "not a complex type");
+ 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.
+ // 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.
+ // An array of 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");
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ bool IsComplex = Ty->isAnyComplexType() && isComplexGnuABI();
+
+ // 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);
+
+ uint64_t TypeSize = getContext().getTypeSize(Ty);
+ uint64_t RegsNeeded = (TypeSize + GPRBits - 1) / GPRBits;
+
+ if (IsComplex || !isAggregateTypeForABI(Ty)) {
+ // Complex values and scalars are passed in GPRs.
+
+ // 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) {
+ // 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.
+ ArgGPRsLeft -= 1;
+ }
+
+ return DefaultABIInfo::classifyArgumentType(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) {
@@ -463,8 +570,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);
}
diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-xlcompat-cmplx.c b/clang/test/CodeGen/PowerPC/builtins-ppc-xlcompat-cmplx.c
index cf03af38b3784..ae224aef66da9 100644
--- a/clang/test/CodeGen/PowerPC/builtins-ppc-xlcompat-cmplx.c
+++ b/clang/test/CodeGen/PowerPC/builtins-ppc-xlcompat-cmplx.c
@@ -6,10 +6,14 @@
// RUN: -emit-llvm %s -o - -target-cpu pwr8 | FileCheck %s --check-prefix=64BITLE
// RUN: %clang_cc1 -triple powerpc64-unknown-aix \
// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s --check-prefix=64BITAIX
-// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu \
-// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s --check-prefix=32BIT
-// RUN: %clang_cc1 -triple powerpcle-unknown-linux-gnu \
-// RUN: -emit-llvm %s -o - -target-cpu pwr8 | FileCheck %s --check-prefix=32BITLE
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fclang-abi-compat=22 \
+// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s --check-prefix=32BIT-CLANG22
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s --check-prefix=32BIT-CLANG23
+// RUN: %clang_cc1 -triple powerpcle-unknown-linux-gnu -fclang-abi-compat=22 \
+// RUN: -emit-llvm %s -o - -target-cpu pwr8 | FileCheck %s --check-prefix=32BITLE-CLANG22
+// RUN: %clang_cc1 -triple powerpcle-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN: -emit-llvm %s -o - -target-cpu pwr8 | FileCheck %s --check-prefix=32BITLE-CLANG23
// RUN: %clang_cc1 -triple powerpc-unknown-aix \
// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s --check-prefix=32BITAIX
@@ -61,49 +65,81 @@
// 64BITAIX-NEXT: [[TMP2:%.*]] = load { double, double }, ptr [[RETVAL]], align 4
// 64BITAIX-NEXT: ret { double, double } [[TMP2]]
//
-// 32BIT-LABEL: @testcmplx(
-// 32BIT-NEXT: entry:
-// 32BIT-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
-// 32BIT-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
-// 32BIT-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
-// 32BIT-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
-// 32BIT-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
-// 32BIT-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
-// 32BIT-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT:%.*]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: store double [[TMP0]], ptr [[AGG_RESULT_REALP]], align 8
-// 32BIT-NEXT: store double [[TMP1]], ptr [[AGG_RESULT_IMAGP]], align 8
-// 32BIT-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
-// 32BIT-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
-// 32BIT-NEXT: store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
-// 32BIT-NEXT: ret void
+// 32BIT-CLANG22-LABEL: @testcmplx(
+// 32BIT-CLANG22-NEXT: entry:
+// 32BIT-CLANG22-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
+// 32BIT-CLANG22-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
+// 32BIT-CLANG22-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
+// 32BIT-CLANG22-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
+// 32BIT-CLANG22-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
+// 32BIT-CLANG22-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT:%.*]], i32 0, i32 0
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BIT-CLANG22-NEXT: store double [[TMP0]], ptr [[AGG_RESULT_REALP]], align 8
+// 32BIT-CLANG22-NEXT: store double [[TMP1]], ptr [[AGG_RESULT_IMAGP]], align 8
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// 32BIT-CLANG22-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BIT-CLANG22-NEXT: store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// 32BIT-CLANG22-NEXT: store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// 32BIT-CLANG22-NEXT: ret void
//
-// 32BITLE-LABEL: @testcmplx(
-// 32BITLE-NEXT: entry:
-// 32BITLE-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
-// 32BITLE-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
-// 32BITLE-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
-// 32BITLE-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
-// 32BITLE-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
-// 32BITLE-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
-// 32BITLE-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT:%.*]], i32 0, i32 0
-// 32BITLE-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BITLE-NEXT: store double [[TMP0]], ptr [[AGG_RESULT_REALP]], align 8
-// 32BITLE-NEXT: store double [[TMP1]], ptr [[AGG_RESULT_IMAGP]], align 8
-// 32BITLE-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BITLE-NEXT: [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
-// 32BITLE-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BITLE-NEXT: [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
-// 32BITLE-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BITLE-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BITLE-NEXT: store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
-// 32BITLE-NEXT: store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
-// 32BITLE-NEXT: ret void
+// 32BIT-CLANG23-LABEL: @testcmplx(
+// 32BIT-CLANG23-NEXT: entry:
+// 32BIT-CLANG23-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// 32BIT-CLANG23-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
+// 32BIT-CLANG23-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
+// 32BIT-CLANG23-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
+// 32BIT-CLANG23-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
+// 32BIT-CLANG23-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
+// 32BIT-CLANG23-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
+// 32BIT-CLANG23-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// 32BIT-CLANG23-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// 32BIT-CLANG23-NEXT: store double [[TMP0]], ptr [[RETVAL_REALP]], align 8
+// 32BIT-CLANG23-NEXT: store double [[TMP1]], ptr [[RETVAL_IMAGP]], align 8
+// 32BIT-CLANG23-NEXT: [[TMP2:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// 32BIT-CLANG23-NEXT: ret [4 x i32] [[TMP2]]
+//
+// 32BITLE-CLANG22-LABEL: @testcmplx(
+// 32BITLE-CLANG22-NEXT: entry:
+// 32BITLE-CLANG22-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
+// 32BITLE-CLANG22-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
+// 32BITLE-CLANG22-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
+// 32BITLE-CLANG22-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
+// 32BITLE-CLANG22-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
+// 32BITLE-CLANG22-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT:%.*]], i32 0, i32 0
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BITLE-CLANG22-NEXT: store double [[TMP0]], ptr [[AGG_RESULT_REALP]], align 8
+// 32BITLE-CLANG22-NEXT: store double [[TMP1]], ptr [[AGG_RESULT_IMAGP]], align 8
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// 32BITLE-CLANG22-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// 32BITLE-CLANG22-NEXT: store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// 32BITLE-CLANG22-NEXT: store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// 32BITLE-CLANG22-NEXT: ret void
+//
+// 32BITLE-CLANG23-LABEL: @testcmplx(
+// 32BITLE-CLANG23-NEXT: entry:
+// 32BITLE-CLANG23-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// 32BITLE-CLANG23-NEXT: [[REAL_ADDR:%.*]] = alloca double, align 8
+// 32BITLE-CLANG23-NEXT: [[IMAG_ADDR:%.*]] = alloca double, align 8
+// 32BITLE-CLANG23-NEXT: store double [[REAL:%.*]], ptr [[REAL_ADDR]], align 8
+// 32BITLE-CLANG23-NEXT: store double [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 8
+// 32BITLE-CLANG23-NEXT: [[TMP0:%.*]] = load double, ptr [[REAL_ADDR]], align 8
+// 32BITLE-CLANG23-NEXT: [[TMP1:%.*]] = load double, ptr [[IMAG_ADDR]], align 8
+// 32BITLE-CLANG23-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// 32BITLE-CLANG23-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// 32BITLE-CLANG23-NEXT: store double [[TMP0]], ptr [[RETVAL_REALP]], align 8
+// 32BITLE-CLANG23-NEXT: store double [[TMP1]], ptr [[RETVAL_IMAGP]], align 8
+// 32BITLE-CLANG23-NEXT: [[TMP2:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// 32BITLE-CLANG23-NEXT: ret [4 x i32] [[TMP2]]
//
// 32BITAIX-LABEL: @testcmplx(
// 32BITAIX-NEXT: entry:
@@ -173,49 +209,81 @@ double _Complex testcmplx(double real, double imag) {
// 64BITAIX-NEXT: [[TMP2:%.*]] = load { float, float }, ptr [[RETVAL]], align 4
// 64BITAIX-NEXT: ret { float, float } [[TMP2]]
//
-// 32BIT-LABEL: @testcmplxf(
-// 32BIT-NEXT: entry:
-// 32BIT-NEXT: [[REAL_ADDR:%.*]] = alloca float, align 4
-// 32BIT-NEXT: [[IMAG_ADDR:%.*]] = alloca float, align 4
-// 32BIT-NEXT: store float [[REAL:%.*]], ptr [[REAL_ADDR]], align 4
-// 32BIT-NEXT: store float [[IMAG:%.*]], ptr [[IMAG_ADDR]], align 4
-// 32BIT-NEXT: [[TMP0:%.*]] = load float, ptr [[REAL_ADDR]], align 4
-// 32BIT-NEXT: [[TMP1:%.*]] = load float, ptr [[IMAG_ADDR]], align 4
-// 32BIT-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT:%.*]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: store float [[TMP0]], ptr [[AGG_RESULT_REALP]], align 4
-// 32BIT-NEXT: store float [[TMP1]], ptr [[AGG_RESULT_IMAGP]], align 4
-// 32BIT-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_REAL:%.*]] = load float, ptr [[AGG_RESULT_REALP1]], align 4
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: [[AGG_RESULT_IMAG:%.*]] = load float, ptr [[AGG_RESULT_IMAGP2]], align 4
-// 32BIT-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
-// 32BIT-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
-// 32BIT-NEXT: store float [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 4
-// 32BIT-NEXT: store float [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 4
-// 32BIT-NEXT: ret void
+// 32BIT-CLANG22-LABEL: @testcmplxf(
+// 32BIT-CLANG22-NEXT: entry:
+// 32BIT-CLANG22-NEXT: [[REAL_ADDR:%.*]] = alloca float, align 4
+// 32BIT-CLANG22-NEXT: [[IMAG_ADDR:%.*]] = alloca float, align 4
+// 32BIT-CLANG22-NEXT: store float [[REAL:%.*]], ptr [[REAL_ADDR]], align 4
+// 32BIT-CLANG22-NEXT: store float [[IMAG:%.*]], ptr [[IMAG...
[truncated]
|
| static const unsigned GPRBits = 32; | ||
|
|
||
| bool isComplexGnuABI() const { | ||
| return !getTarget().getTriple().isOSDarwin() && |
There was a problem hiding this comment.
@jrtc27, should the opt-out apply also to FreeBSD ppc32?
There was a problem hiding this comment.
Probably not, FreeBSD tends to follow Linux in these matters? But IANA PowerPC person (nor really a floating-point person). CC @chmeeedalf.
|
What is maybe not clear from the issue and PR description is that this ABI mismatch means libcalls are just broken in practice, e.g. // main.c
#include <stdio.h>
#include <complex.h>
int main(void) {
volatile float re = -1.0f, im = 0.0f;
_Complex float s = csqrtf(re + im * I);
printf("%f + %fi\n", crealf(s), cimagf(s));
return 0;
}with powerpc-linux-gnu-gcc -O2 main.c -o gcc.out -lm
# Use clang to compile, GCC as the linker.
clang --target=powerpc-linux-gnu -O2 -c main.c -o clang.o
powerpc-linux-gnu-gcc clang.o -o clang.out -lm
echo "gcc"
qemu-ppc -L /usr/powerpc-linux-gnu ./gcc.out
echo "clang"
qemu-ppc -L /usr/powerpc-linux-gnu ./clang.outprints The clang result is nonsense. |
7f395d8 to
b35bb13
Compare
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
Modify the ABI of `_Complex` so that it matches GCC for all types, specifically: - On SPARC, a `_Complex` value with an integer element type is now passed and returned packed into the one or two integer registers it fits in, matching GCC. Clang previously passed such a value indirectly and returned it with one part per register. `-fclang-abi-compat=23` restores the previous behavior. - On SPARC64, a `_Complex char` or `_Complex short` is now right-justified in its slot in the parameter array, like every other scalar narrower than a slot, rather than left-justified the way a small struct is. `-fclang-abi-compat=23` restores the previous behavior. Complex integers are a GNU extension, but generally clang is compatible with GCC. Really, you might as well be, deviating can only bite users. I've now validated the implementation with https://github.com/folkertdev/powerpc-complex-abi-validation which compiles various signatures using `_Complex` with GCC and Clang and checks that values make it from one side to the other. related: - rust-lang/rust#154023 - #208917 - #212119
Modify the ABI of `_Complex` so that it matches GCC for all types, specifically: - On SPARC, a `_Complex` value with an integer element type is now passed and returned packed into the one or two integer registers it fits in, matching GCC. Clang previously passed such a value indirectly and returned it with one part per register. `-fclang-abi-compat=23` restores the previous behavior. - On SPARC64, a `_Complex char` or `_Complex short` is now right-justified in its slot in the parameter array, like every other scalar narrower than a slot, rather than left-justified the way a small struct is. `-fclang-abi-compat=23` restores the previous behavior. Complex integers are a GNU extension, but generally clang is compatible with GCC. Really, you might as well be, deviating can only bite users. I've now validated the implementation with https://github.com/folkertdev/powerpc-complex-abi-validation which compiles various signatures using `_Complex` with GCC and Clang and checks that values make it from one side to the other. related: - rust-lang/rust#154023 - llvm/llvm-project#208917 - llvm/llvm-project#212119
Modify the ABI of `_Complex` so that it matches GCC for all types, specifically: - On SPARC, a `_Complex` value with an integer element type is now passed and returned packed into the one or two integer registers it fits in, matching GCC. Clang previously passed such a value indirectly and returned it with one part per register. `-fclang-abi-compat=23` restores the previous behavior. - On SPARC64, a `_Complex char` or `_Complex short` is now right-justified in its slot in the parameter array, like every other scalar narrower than a slot, rather than left-justified the way a small struct is. `-fclang-abi-compat=23` restores the previous behavior. Complex integers are a GNU extension, but generally clang is compatible with GCC. Really, you might as well be, deviating can only bite users. I've now validated the implementation with https://github.com/folkertdev/powerpc-complex-abi-validation which compiles various signatures using `_Complex` with GCC and Clang and checks that values make it from one side to the other. related: - rust-lang/rust#154023 - llvm/llvm-project#208917 - llvm/llvm-project#212119
Modify the ABI of `_Complex` so that it matches GCC for all types, specifically: - On SPARC, a `_Complex` value with an integer element type is now passed and returned packed into the one or two integer registers it fits in, matching GCC. Clang previously passed such a value indirectly and returned it with one part per register. `-fclang-abi-compat=23` restores the previous behavior. - On SPARC64, a `_Complex char` or `_Complex short` is now right-justified in its slot in the parameter array, like every other scalar narrower than a slot, rather than left-justified the way a small struct is. `-fclang-abi-compat=23` restores the previous behavior. Complex integers are a GNU extension, but generally clang is compatible with GCC. Really, you might as well be, deviating can only bite users. I've now validated the implementation with https://github.com/folkertdev/powerpc-complex-abi-validation which compiles various signatures using `_Complex` with GCC and Clang and checks that values make it from one side to the other. related: - rust-lang/rust#154023 - llvm#208917 - llvm#212119
067d939 to
9d89852
Compare
closes #77732
cc #44482
Replaces #77732 and truly fixes #56023.
The design, as per #77732 (comment), is that
-fclang-abi-compat=22continues to give the old ABI behavior, and in 23 (or 24, we're close to the cutoff and this might not make it) gives the new GCC-compatible behavior.So, the challenge is of course how to test and review all this. I have a collection of input file that I used for testing empirically versus GCC:
https://github.com/folkertdev/powerpc-complex-abi-validation
I'd happily just add (a subset of) those and run the
CHECKgeneration on them, but I'm not sure that wall of text is worth it: you can't really see the ABI the LLVM IR, and you certainly can't see easily that it's equivalent to GCC. On the other hand, as you can see from the current tests that were touched, the testing is rather thin.And the GCC logic is non-trivial: some types must be converted to integers, vectors or arrays to get the right behavior. There is register alignment for some types.
Future work is to make
va_argwork. The pre-existing implementation has a big FIXME for it, but it should be possible to implement in a GCC-compatible way now.