Skip to content
11 changes: 11 additions & 0 deletions clang/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ honored, and calls use the caller's features, matching GCC. Per-function
features cannot lower the translation-unit ABI level;
`-fclang-abi-compat=23` restores the previous behavior. (#GH193298)

- On SPARC, a `_Complex` value with an integer element type is now passed and
returned packed into the one or two integer registers it fits in, matching GCC.
Clang previously passed such a value indirectly and returned it with one part
per register.
`-fclang-abi-compat=23` restores the previous behavior. (#GH212340)

- On SPARC64, a `_Complex char` or `_Complex short` is now
right-justified in its slot in the parameter array, like every other scalar
narrower than a slot, rather than left-justified the way a small struct is.
`-fclang-abi-compat=23` restores the previous behavior. (#GH212340)

- On MIPS, a `_Complex` value with an integer element type is now returned packed
into a single integer register when it fits in one, matching GCC. A `_Complex char` or
`_Complex short`, and on N32/N64 also a `_Complex int`, is no longer returned
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/ABIVersions.def
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ ABI_VER_MAJOR(22)
/// This causes clang to:
/// - Ignore per-function target attributes when determining the x86 AVX ABI
/// level.
/// - On SPARC, pass a `_Complex` value with an integer element type
/// indirectly, and return it with one part per integer register, instead of
/// packing it into the one or two registers it fits in.
/// - On SPARC64, left-justify a `_Complex` value with an integer element type
/// that is narrower than a parameter array slot, instead of right-justifying
/// it the way every other sub-slot scalar is passed.
/// - On MIPS, return a `_Complex` value with an integer element type with one
/// part per integer register, instead of packing it into a single register
/// where it fits.
Expand Down
83 changes: 68 additions & 15 deletions clang/lib/CodeGen/Targets/Sparc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,67 @@ using namespace clang::CodeGen;
namespace {
class SparcV8ABIInfo : public DefaultABIInfo {
public:
SparcV8ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
SparcV8ABIInfo(CodeGenTypes &CGT)
: DefaultABIInfo(CGT),
IsComplexGnuABI(!CGT.getContext().getLangOpts().isCompatibleWith(
LangOptions::ClangABI::Ver23)) {}

private:
/// Whether how `_Complex` values are passed and returned is GCC-compatible.
bool IsComplexGnuABI;

ABIArgInfo classifyComplexType(const ComplexType *Ty, bool IsRet) const;
ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType Ty) const;
void computeInfo(CGFunctionInfo &FI) const override;
};
} // end anonymous namespace

ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
const auto *CT = Ty->getAs<ComplexType>();
const auto *BT = Ty->getAs<BuiltinType>();
if (CT)
BT = CT->getElementType()->getAs<BuiltinType>();
bool IsLongDouble = BT && BT->getKind() == BuiltinType::LongDouble;
ABIArgInfo SparcV8ABIInfo::classifyComplexType(const ComplexType *CT,
bool IsRet) const {
QualType ElementTy = CT->getElementType();

if (IsComplexGnuABI && ElementTy->isIntegerType()) {
// The default path already does the right thing for `long long _Complex`.
uint64_t ElementTypeSize = getContext().getTypeSize(ElementTy);
if (ElementTypeSize <= 32) {
// Coerce to an integer to get the correct scalar-like behavior.
return ABIArgInfo::getDirect(
llvm::IntegerType::get(getVMContext(), 2 * ElementTypeSize));
}
}

// Any other complex value is passed indirectly, but returned in registers.
if (!IsRet)
return getNaturalAlignIndirect(QualType(CT, 0),
getDataLayout().getAllocaAddrSpace());

// long double _Complex is special in that it should be marked as inreg.
if (CT)
return IsLongDouble ? ABIArgInfo::getDirectInReg()
: ABIArgInfo::getDirect();
// long double _Complex is special, it is marked as inreg.
const auto *BT = ElementTy->getAs<BuiltinType>();
if (BT && BT->getKind() == BuiltinType::LongDouble)
return ABIArgInfo::getDirectInReg();

if (IsLongDouble)
return ABIArgInfo::getDirect();
}

ABIArgInfo SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
if (const auto *CT = Ty->getAs<ComplexType>())
return classifyComplexType(CT, /*IsRet=*/true);

if (const auto *BT = Ty->getAs<BuiltinType>();
BT && BT->getKind() == BuiltinType::LongDouble)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
/*ByVal=*/false);

return DefaultABIInfo::classifyReturnType(Ty);
}

ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
if (const auto *BT = Ty->getAs<BuiltinType>();
BT && BT->getKind() == BuiltinType::LongDouble)
if (const auto *CT = Ty->getAs<ComplexType>())
return classifyComplexType(CT, /*IsRet=*/false);

const auto *BT = Ty->getAs<BuiltinType>();
if (BT && BT->getKind() == BuiltinType::LongDouble)
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());

return DefaultABIInfo::classifyArgumentType(Ty);
Expand Down Expand Up @@ -123,9 +153,15 @@ class SparcV8TargetCodeGenInfo : public TargetCodeGenInfo {
namespace {
class SparcV9ABIInfo : public ABIInfo {
public:
SparcV9ABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {}
SparcV9ABIInfo(CodeGenTypes &CGT)
: ABIInfo(CGT),
IsComplexGnuABI(!CGT.getContext().getLangOpts().isCompatibleWith(
LangOptions::ClangABI::Ver23)) {}

private:
/// Whether how `_Complex` values are passed and returned is GCC-compatible.
bool IsComplexGnuABI;

ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit,
unsigned &RegOffset) const;
void computeInfo(CGFunctionInfo &FI) const override;
Expand Down Expand Up @@ -294,6 +330,23 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit,
return ABIArgInfo::getExtend(Ty, /*T=*/nullptr, Padding);
}

// When being GCC-compatible, cast a complex char, short and int to an integer
// type of the right size to get the correct scalar-like behavior. Other
// complex types fall through and are treated like a struct containing the
// real and imaginary parts, e.g. `{ i64, i64 }` or `{ double, double }`.
if (IsComplexGnuABI) {
const auto *CT = Ty->getAs<ComplexType>();
if (CT && CT->getElementType()->isIntegerType()) {
uint64_t ElementTypeSize = Context.getTypeSize(CT->getElementType());
if (ElementTypeSize <= 32) {
RegOffset += 1;
return ABIArgInfo::getDirect(
llvm::IntegerType::get(VMContext, 2 * ElementTypeSize),
/*Offset=*/0, Padding);
}
}
}

// Other non-aggregates go in registers.
if (!isAggregateTypeForABI(Ty)) {
RegOffset += PaddingSlots + SizeSlots;
Expand Down
Loading
Loading