Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments #77732

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,
Copy link
Contributor

Choose a reason for hiding this comment

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

it looks you define the CMPLX_Default and CMPLX_OnStack , but never use them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was for future reference, if any arch wants to use this.

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 @@ -2601,6 +2601,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
87 changes: 81 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 GPRBits = 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,73 @@ 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());
Copy link
Contributor

@diggerlin diggerlin Feb 22, 2024

Choose a reason for hiding this comment

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

I am curiously why not get ElemTy = llvm::Type::getInt64Ty(getVMContext()); instead of ElemTy = llvm::Type::getInt32Ty(getVMContext());

and change following code as

llvm::Type *ElemTy = llvm::Type::getInt64Ty(getVMContext()): 
unsigned SizeRegs = TypeSize/64;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reason for using llvm::Type::getInt64Ty(getVMContext()) for floats here was to follow ABI ATR-PASS-COMPLEX-IN-GPRS

complex single-precision float : If gr is even, set gr = gr + 1. Load the lower-addressed word of the
argument into gr and the higher-addressed word into gr + 1, set gr = gr + 2.

complex double-precision float: Load the words of the argument, in memory-address order, into gr, gr + 1,
gr + 2 and gr + 3, set gr = gr + 4.

You can check the previous discussion here. : https://reviews.llvm.org/D146942

Copy link
Contributor Author

Choose a reason for hiding this comment

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

llvm::Type *ElemTy = llvm::Type::getInt64Ty(getVMContext());
unsigned SizeRegs = TypeSize/64;

Since Typesize will be 64, in if is it necessary to replace 1 to TypeSize/64?

Copy link
Contributor

Choose a reason for hiding this comment

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

got it, thanks for explain. can you add your explain as comment ?

RegsNeeded = TypeSize >> 5;
}
return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, RegsNeeded));
}

ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
int &ArgGPRsLeft) const {
Ty = useFirstFieldIfTransparentUnion(Ty);
bool IsComplex = Ty->isAnyComplexType();

if ((getCodeGenOpts().getComplexInRegABI() != CodeGenOptions::CMPLX_InGPR) ||
!ArgGPRsLeft || (!IsComplex && Ty->isFloatingType() && !IsSoftFloatABI))
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);

// For complex type or any other primitive types.
if (IsComplex || !isAggregateTypeForABI(Ty)) {
// If gr is even set gr = gr + 1 for TypeSize=64.
if (TypeSize == 64 && ArgGPRsLeft % 2 == 1)
--ArgGPRsLeft;

if (TypeSize <= GPRBits * ArgGPRsLeft) {
ArgGPRsLeft -= TypeSize / GPRBits;
if (IsComplex)
return handleComplex(TypeSize);
}
} else
--ArgGPRsLeft; // Records with non-trivial destructors/copy-constructors
// should not be passed by value.

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 +491,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 +505,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 @@ -5651,6 +5651,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)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need a test case for this.

<< 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 @@ -1654,6 +1654,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 @@ -2028,6 +2032,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