[Sparc][clang] make _Complex ABI GCC-compatible - #212340
Conversation
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: Folkert de Vries (folkertdev) ChangesModify the ABI of
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 related:
Full diff: https://github.com/llvm/llvm-project/pull/212340.diff 4 Files Affected:
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) {}
|
|
@llvm/pr-subscribers-backend-sparc Author: Folkert de Vries (folkertdev) ChangesModify the ABI of
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 related:
Full diff: https://github.com/llvm/llvm-project/pull/212340.diff 4 Files Affected:
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) {}
|
9d73e4d to
27c742a
Compare
| // V9-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2 | ||
| // V9-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2 |
There was a problem hiding this comment.
I suspect the order of parts should be reversed on little-endian SPARC
[offtopic]
but is sparc-el even real?.. gcc doesn't think so. I wonder if sparc-el support in clang/llvm is actually functional and whether we can drop it.
There was a problem hiding this comment.
This PR does not touch sparcel codegen at all
llvm-project/clang/lib/CodeGen/CodeGenModule.cpp
Lines 296 to 299 in b1e21e2
That falls through to the default sparcel. So, I don't think that EL sparc is relevant for this PR (there is no GCC to be compatible with anyway).
There was a problem hiding this comment.
That falls through to the default
sparcel.
Interesting. So we don't support it properly after all
folkertdev
left a comment
There was a problem hiding this comment.
I'm not sure we need
-fclang-abi-compatflag, but I'm not an interested party.
It's just a courtesy, and quite cheap to support. I'm happy to remove it though if it's not required. (it was the recommended strategy for a similar change in the powerpc backend, see #77732 (comment))
Also, are
_Complex typespassed in memory the same way? (when we'are out of argument registers)
the same way as what?
| // V9-NEXT: store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2 | ||
| // V9-NEXT: store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2 |
There was a problem hiding this comment.
This PR does not touch sparcel codegen at all
llvm-project/clang/lib/CodeGen/CodeGenModule.cpp
Lines 296 to 299 in b1e21e2
That falls through to the default sparcel. So, I don't think that EL sparc is relevant for this PR (there is no GCC to be compatible with anyway).
The same way as in registers. When, e.g., |
|
Yes it's the same, that is what the implementation already does, and it's consistent with GCC. |
s-barannikov
left a comment
There was a problem hiding this comment.
LGTM, thanks
Please wait a few days in case other reviewers have something to add
|
|
||
| // When being GCC-compatible, cast a complex char, short and int to an integer | ||
| // type of the right size to get the correct scalar-like behavior. Other | ||
| // comple types fall through and are treated like any other struct, e.g. |
There was a problem hiding this comment.
| // comple types fall through and are treated like any other struct, e.g. | |
| // complex types fall through and are treated like any other struct, e.g. |
(nit) "like any other struct" is a bit misleading because _Complex is not a struct type. Maybe something like "like a struct containing the real and imaginary parts"?
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
Modify the ABI of
_Complexso that it matches GCC for all types, specifically:On SPARC, a
_Complexvalue with an integer element type is now passed andreturned 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=23restores the previous behavior.On SPARC64, a
_Complex charor_Complex shortis nowright-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=23restores 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
_Complexwith GCC and Clang and checks that values make it from one side to the other.related:
complex_numbers) rust-lang/rust#154023_ComplexABI GCC-compatible #208917_ComplexABI match GCC #212119