Skip to content

Commit

Permalink
[clang][PowerPC] Add flag to enable compatibility with GNU for comple…
Browse files Browse the repository at this point in the history
…x arguments

Fixes : #56023

https://godbolt.org/z/1bsW1sKMs

newFlag : -fcomplex-ppc-gnu-abi

GNU uses GPRs for complex parameters and return values storing for PowerPC-32bit,
which can be enabled which above flag.
Intent of this patch is to make clang compatible with GNU libraries of complex.
  • Loading branch information
Long5hot committed Mar 28, 2024
1 parent e640d9e commit 3c5fcb0
Show file tree
Hide file tree
Showing 8 changed files with 861 additions and 6 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.

/// If -fpcc-struct-return or -freg-struct-return is specified.
ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
/// If -fcomplex-ppc-gnu-abi is specified on ppc32.
ENUM_CODEGENOPT(ComplexInRegABI, ComplexArgumentConventionKind, 2, CMPLX_OnStack)

CODEGENOPT(RelaxAll , 1, 0) ///< Relax all machine code instructions.
CODEGENOPT(RelaxedAliasing , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ class CodeGenOptions : public CodeGenOptionsBase {
SRCK_InRegs // Small structs in registers (-freg-struct-return).
};

enum ComplexArgumentConventionKind {
CMPLX_OnStack,
CMPLX_InGPR, // If -fcomplex-ppc-gnu-abi is specified on ppc32
CMPLX_InFPR
};

enum ProfileInstrKind {
ProfileNone, // Profile instrumentation is turned off.
ProfileClangInstr, // Clang instrumentation to generate execution counts
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2577,6 +2577,10 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
HelpText<"Form fused FP ops (e.g. FMAs)">,
Values<"fast,on,off,fast-honor-pragmas">;

def fcomplex_ppc_gnu_abi : Flag<["-"], "fcomplex-ppc-gnu-abi">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
DocBrief<"Follow the GNU ABI, pass Complex values in GPRs instead of the stack for PowerPC-32">,
HelpText<"Pass Complex values in GPR instead of stack for PowerPC-32">;

defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
NegFlag<SetFalse, [], [ClangOption, CC1Option],
Expand Down
94 changes: 88 additions & 6 deletions clang/lib/CodeGen/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,12 @@ namespace {
class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
bool IsSoftFloatABI;
bool IsRetSmallStructInRegABI;
// Size of GPR in bits.
static const unsigned RegLen = 32;
static const int ArgGPRsNum = 8;

CharUnits getParamTypeAlignment(QualType Ty) const;
ABIArgInfo handleComplex(uint64_t &TypeSize) const;

public:
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
Expand All @@ -340,12 +344,17 @@ 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 = ArgGPRsNum;

for (auto &I : FI.arguments())
I.info = classifyArgumentType(I.type);
I.info = classifyArgumentType(I.type, ArgGPRsLeft);
}

Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
Expand Down Expand Up @@ -396,12 +405,80 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}

ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(uint64_t &TypeSize) const {
llvm::Type *ElemTy;
unsigned RegsNeeded; // Registers Needed for Complex.

// Choice of using llvm::Type::getInt64Ty(getVMContext()) for complex
// single-precision floats is based on the ABI ATR-PASS-COMPLEX-IN-GPRS
// specification. According to the specification:
// - For complex single-precision floats: If the register (gr) is even, it's
// incremented by one, and the lower-addressed word of the argument is loaded
// into gr, while the higher-addressed word is loaded into gr + 1. Then, gr is
// incremented by 2.
// - For complex double-precision floats: The words of the argument are loaded
// in memory-address order into gr, gr + 1, gr + 2, and gr + 3, with gr being
// incremented by 4. Thus, to maintain even alignment and adhere to the ABI
// specification, llvm::Type::getInt64Ty(getVMContext()) is used when TypeSize
// is 64. Powerpc backend handles this alignment requirement. Specifically,
// you can refer to the CC_PPC32_SVR4_Custom_AlignArgRegs method from
// PPCCallingconvention.cpp. For more context, refer to the previous
// discussion: https://reviews.llvm.org/D146942 and the related LLVM pull
// request: #77732

if (TypeSize == 64) {
ElemTy = llvm::Type::getInt64Ty(getVMContext());
RegsNeeded = 1;
} else {
ElemTy = llvm::Type::getInt32Ty(getVMContext());
RegsNeeded = TypeSize >> 5;
}
return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, RegsNeeded));
}

ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
int &ArgGPRsLeft) const {
Ty = useFirstFieldIfTransparentUnion(Ty);

if (!(getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_InGPR) ||
!ArgGPRsLeft)
return DefaultABIInfo::classifyArgumentType(Ty);

assert(ArgGPRsLeft >= 0 && "Arg GPR must be large or equal than zero");
ASTContext &Context = getContext();
uint64_t TypeSize = Context.getTypeSize(Ty);

if (Ty->isAnyComplexType()) {
// If gr is even set gr = gr + 1 for TypeSize=64.
if (TypeSize == 64 && ArgGPRsLeft % 2 == 1)
--ArgGPRsLeft;

if (TypeSize <= RegLen * ArgGPRsLeft) {
ArgGPRsLeft -= TypeSize / RegLen;
return handleComplex(TypeSize);
}
}

// Records with non-trivial destructors/copy-constructors should not be
// passed by value.
if (isAggregateTypeForABI(Ty))
--ArgGPRsLeft;
else if (!Ty->isFloatingType() || (Ty->isFloatingType() && IsSoftFloatABI)) {
// For other primitive types.
if (TypeSize == 64 && ArgGPRsLeft % 2 == 1)
--ArgGPRsLeft; // If gr is even set gr = gr + 1 for TypeSize=64.
if (TypeSize <= ArgGPRsLeft * RegLen)
ArgGPRsLeft -= TypeSize / RegLen;
}

return DefaultABIInfo::classifyArgumentType(Ty);
}

ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
uint64_t Size;
uint64_t Size = getContext().getTypeSize(RetTy);

// -msvr4-struct-return puts small aggregates in GPR3 and GPR4.
if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI &&
(Size = getContext().getTypeSize(RetTy)) <= 64) {
if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI && Size <= 64) {
// System V ABI (1995), page 3-22, specified:
// > A structure or union whose size is less than or equal to 8 bytes
// > shall be returned in r3 and r4, as if it were first stored in the
Expand All @@ -421,6 +498,10 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
}
}

if ((getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_InGPR) &&
RetTy->isAnyComplexType())
return handleComplex(Size);

return DefaultABIInfo::classifyReturnType(RetTy);
}

Expand All @@ -431,11 +512,12 @@ Address PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
if (getTarget().getTriple().isOSDarwin()) {
auto TI = getContext().getTypeInfoInChars(Ty);
TI.Align = getParamTypeAlignment(Ty);
int ArgGPRs = ArgGPRsNum;

CharUnits SlotSize = CharUnits::fromQuantity(4);
return emitVoidPtrVAArg(CGF, VAList, Ty,
classifyArgumentType(Ty).isIndirect(), TI, SlotSize,
/*AllowHigherAlign=*/true);
classifyArgumentType(Ty, ArgGPRs).isIndirect(), TI,
SlotSize, /*AllowHigherAlign=*/true);
}

const unsigned OverflowLimit = 8;
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5585,6 +5585,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}
}

if (Arg *A = Args.getLastArg(options::OPT_fcomplex_ppc_gnu_abi)) {
if (!TC.getTriple().isPPC32() || !TC.getTriple().isOSBinFormatELF()) {
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getSpelling() << RawTriple.str();
} else {
CmdArgs.push_back("-fcomplex-ppc-gnu-abi");
}
}

if (Args.hasFlag(options::OPT_mrtd, options::OPT_mno_rtd, false)) {
if (Triple.getArch() == llvm::Triple::m68k)
CmdArgs.push_back("-fdefault-calling-conv=rtdcall");
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1651,6 +1651,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
GenerateArg(Consumer, Opt);
}

if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR) {
GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);
}

if (Opts.EnableAIXExtendedAltivecABI)
GenerateArg(Consumer, OPT_mabi_EQ_vec_extabi);

Expand Down Expand Up @@ -2023,6 +2027,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
}
}

if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) {
Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR);
}

if (Arg *A = Args.getLastArg(OPT_mxcoff_roptr)) {
if (!T.isOSAIX())
Diags.Report(diag::err_drv_unsupported_opt_for_target)
Expand Down
Loading

0 comments on commit 3c5fcb0

Please sign in to comment.