Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ features cannot lower the translation-unit ABI level;
always passed the parts separately. `-fclang-abi-compat=23` restores the previous
behavior. (#GH212109)

- On PowerPC (32-bit), the ABI of `_Complex` now matches GCC.
`-fclang-abi-compat=23` restores the previous behavior. (#GH208917)

### AST Dumping Potentially Breaking Changes

### Clang Frontend Potentially Breaking Changes
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/ABIVersions.def
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ ABI_VER_MAJOR(22)
/// - On MIPS N32/N64, always pass a `_Complex float` or `_Complex double`
/// argument as its two parts, one floating-point register each, instead of
/// packing it into integer registers once there is no room for both.
/// - On PowerPC (32-bit), treat `_Complex` like a struct of two scalar fields.
ABI_VER_MAJOR(23)

/// Conform to the underlying platform's C and C++ ABIs as closely as we can.
Expand Down
105 changes: 103 additions & 2 deletions clang/lib/CodeGen/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,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() &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrtc27, should the opt-out apply also to FreeBSD ppc32?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, FreeBSD tends to follow Linux in these matters? But IANA PowerPC person (nor really a floating-point person). CC @chmeeedalf.

!getContext().getLangOpts().isCompatibleWith(
LangOptions::ClangABI::Ver23);
}

CharUnits getParamTypeAlignment(QualType Ty) const;
ABIArgInfo classifyComplexType(QualType Ty) const;

public:
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
Expand All @@ -371,12 +382,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,
Expand Down Expand Up @@ -427,9 +441,94 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}

ABIArgInfo PPC32_SVR4_ABIInfo::classifyComplexType(QualType Ty) const {
assert(Ty->isAnyComplexType() && "not a complex type");

uint64_t Size = getContext().getTypeSize(Ty);
llvm::LLVMContext &VMC = getVMContext();
llvm::Type *I32 = llvm::Type::getInt32Ty(VMC);

// 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) {
Expand Down Expand Up @@ -464,8 +563,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);
}

Expand Down
Loading
Loading