[clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments - #77732
[clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments#77732Long5hot wants to merge 1 commit into
Conversation
|
@llvm/pr-subscribers-clang-driver @llvm/pr-subscribers-clang-codegen Author: Kishan Parmar (Long5hot) ChangesPatch is 23.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/77732.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 2c4fb6745bc172..beeefae15c63f8 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -213,6 +213,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 for ppc32
+ENUM_CODEGENOPT(ComplexInRegABI, ComplexArgumentConventionKind, 2, CMPLX_Default)
CODEGENOPT(RelaxAll , 1, 0) ///< Relax all machine code instructions.
CODEGENOPT(RelaxedAliasing , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 6952b48e898a81..8abca0e3dda334 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -78,6 +78,13 @@ class CodeGenOptions : public CodeGenOptionsBase {
SRCK_InRegs // Small structs in registers (-freg-struct-return).
};
+ enum ComplexArgumentConventionKind {
+ CMPLX_Default,
+ CMPLX_OnStack,
+ CMPLX_OnGPR, // if ppc32 -fcomplex-ppc-gnu-abi
+ CMPLX_OnFPR
+ };
+
enum ProfileInstrKind {
ProfileNone, // Profile instrumentation is turned off.
ProfileClangInstr, // Clang instrumentation to generate execution counts
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 19becba4a5ad83..251bcb8c49782f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2540,6 +2540,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, store Complex values in GPR instead of stack for PowerPC-32">,
+ HelpText<"Store 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],
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4fd32337cccc09..003848984d5f7a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -184,11 +184,13 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
bool IsSoftFloat =
CodeGenOpts.FloatABI == "soft" || Target.hasFeature("spe");
- return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
+ unsigned RLen = Target.getPointerWidth(LangAS::Default);
+ return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen);
}
case llvm::Triple::ppcle: {
bool IsSoftFloat = CodeGenOpts.FloatABI == "soft";
- return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
+ unsigned RLen = Target.getPointerWidth(LangAS::Default);
+ return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen);
}
case llvm::Triple::ppc64:
if (Triple.isOSAIX())
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index 0c0781a2d5ab9d..89ee2965f6f2e1 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -486,7 +486,8 @@ std::unique_ptr<TargetCodeGenInfo>
createAIXTargetCodeGenInfo(CodeGenModule &CGM, bool Is64Bit);
std::unique_ptr<TargetCodeGenInfo>
-createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI);
+createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI,
+ unsigned RLen);
std::unique_ptr<TargetCodeGenInfo>
createPPC64TargetCodeGenInfo(CodeGenModule &CGM);
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 40dddde508c177..f4885a927ab0ba 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -271,22 +271,33 @@ namespace {
class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
bool IsSoftFloatABI;
bool IsRetSmallStructInRegABI;
+ bool isComplexInRegABI;
+ // Size of GPR in bits
+ unsigned RLen;
+ static const int NumArgGPRs = 8;
CharUnits getParamTypeAlignment(QualType Ty) const;
+ ABIArgInfo handleComplex(QualType Ty, uint64_t &TypeSize) const;
public:
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
- bool RetSmallStructInRegABI)
+ bool RetSmallStructInRegABI, unsigned RLen,
+ bool ComplexInRegABI)
: DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI),
- IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
+ IsRetSmallStructInRegABI(RetSmallStructInRegABI),
+ isComplexInRegABI(ComplexInRegABI), RLen(RLen) {}
ABIArgInfo classifyReturnType(QualType RetTy) const;
+ ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const;
void computeInfo(CGFunctionInfo &FI) const override {
+
+ int ArgGPRsLeft = NumArgGPRs;
+
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
for (auto &I : FI.arguments())
- I.info = classifyArgumentType(I.type);
+ I.info = classifyArgumentType(I.type, ArgGPRsLeft);
}
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
@@ -296,9 +307,11 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
class PPC32TargetCodeGenInfo : public TargetCodeGenInfo {
public:
PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI,
- bool RetSmallStructInRegABI)
+ bool RetSmallStructInRegABI, unsigned RLen,
+ bool ComplexInRegABI)
: TargetCodeGenInfo(std::make_unique<PPC32_SVR4_ABIInfo>(
- CGT, SoftFloatABI, RetSmallStructInRegABI)) {}
+ CGT, SoftFloatABI, RetSmallStructInRegABI, RLen, ComplexInRegABI)) {
+ }
static bool isStructReturnInRegABI(const llvm::Triple &Triple,
const CodeGenOptions &Opts);
@@ -337,12 +350,77 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
return CharUnits::fromQuantity(4);
}
+ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty,
+ uint64_t &TypeSize) const {
+
+ assert(Ty->isAnyComplexType());
+ llvm::Type *ElemTy;
+ unsigned SizeRegs;
+
+ if (TypeSize == 64) {
+ ElemTy = llvm::Type::getInt64Ty(getVMContext());
+ SizeRegs = 1;
+ } else {
+ ElemTy = llvm::Type::getInt32Ty(getVMContext());
+ SizeRegs = (TypeSize + 31) / 32;
+ }
+ return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, SizeRegs));
+}
+
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
+ int &ArgGPRsLeft) const {
+
+ assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ ASTContext &Context = getContext();
+
+ uint64_t TypeSize = Context.getTypeSize(Ty);
+
+ if (isComplexInRegABI && Ty->isAnyComplexType() &&
+ TypeSize <= RLen * ArgGPRsLeft) {
+ ArgGPRsLeft -= TypeSize / RLen;
+ return handleComplex(Ty, TypeSize);
+ }
+
+ if (isAggregateTypeForABI(Ty)) {
+ if (ArgGPRsLeft)
+ ArgGPRsLeft -= 1;
+ // Records with non-trivial destructors/copy-constructors should not be
+ // passed by value.
+ if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
+ return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);
+ }
+ return getNaturalAlignIndirect(Ty);
+ }
+
+ if (!Ty->isFloatingType()) {
+ if (TypeSize > RLen && TypeSize <= 2 * RLen)
+ ArgGPRsLeft -= 2;
+ else
+ ArgGPRsLeft--;
+ }
+
+ // Treat an enum type as its underlying type.
+ if (const EnumType *EnumTy = Ty->getAs<EnumType>())
+ Ty = EnumTy->getDecl()->getIntegerType();
+
+ if (const auto *EIT = Ty->getAs<BitIntType>())
+ if (EIT->getNumBits() >
+ Context.getTypeSize(Context.getTargetInfo().hasInt128Type()
+ ? Context.Int128Ty
+ : Context.LongLongTy))
+ return getNaturalAlignIndirect(Ty);
+
+ return (isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty)
+ : ABIArgInfo::getDirect());
+}
+
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
@@ -361,6 +439,9 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
return ABIArgInfo::getDirect(CoerceTy);
}
}
+ if(isComplexInRegABI && RetTy->isAnyComplexType()) {
+ return handleComplex(RetTy, Size);
+ }
return DefaultABIInfo::classifyReturnType(RetTy);
}
@@ -372,11 +453,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 = NumArgGPRs;
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;
@@ -974,11 +1056,15 @@ CodeGen::createAIXTargetCodeGenInfo(CodeGenModule &CGM, bool Is64Bit) {
}
std::unique_ptr<TargetCodeGenInfo>
-CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI) {
+CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI,
+ unsigned RLen) {
bool RetSmallStructInRegABI = PPC32TargetCodeGenInfo::isStructReturnInRegABI(
CGM.getTriple(), CGM.getCodeGenOpts());
+ bool isComplexInRegABI = (CGM.getCodeGenOpts().getComplexInRegABI() ==
+ CodeGenOptions::CMPLX_OnGPR);
return std::make_unique<PPC32TargetCodeGenInfo>(CGM.getTypes(), SoftFloatABI,
- RetSmallStructInRegABI);
+ RetSmallStructInRegABI, RLen,
+ isComplexInRegABI);
}
std::unique_ptr<TargetCodeGenInfo>
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 2d8ef841d4f6be..47ad15c100ef45 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5454,6 +5454,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");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 11f3f2c2d6425c..5c1f7da805db87 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1640,6 +1640,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
GenerateArg(Consumer, Opt);
}
+ if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_OnGPR) {
+ GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);
+ }
+
if (Opts.EnableAIXExtendedAltivecABI)
GenerateArg(Consumer, OPT_mabi_EQ_vec_extabi);
@@ -2034,6 +2038,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
}
}
+ if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) {
+ Opts.setComplexInRegABI(CodeGenOptions::CMPLX_OnGPR);
+ }
+
if (Arg *A = Args.getLastArg(OPT_mxcoff_roptr)) {
if (!T.isOSAIX())
Diags.Report(diag::err_drv_unsupported_opt_for_target)
diff --git a/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
new file mode 100644
index 00000000000000..b0df1dc836ba81
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
@@ -0,0 +1,132 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 -fcomplex-ppc-gnu-abi \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-GNU
+
+// CHECK-DEF-LABEL: define dso_local void @foo1
+// CHECK-DEF-SAME: (ptr noalias sret({ float, float }) align 4 [[AGG_RESULT:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-DEF-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: store float [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 4
+// CHECK-DEF-NEXT: store float [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 4
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_REAL:%.*]] = load float, ptr [[AGG_RESULT_REALP1]], align 4
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAG:%.*]] = load float, ptr [[AGG_RESULT_IMAGP2]], align 4
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: store float [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 4
+// CHECK-DEF-NEXT: store float [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 4
+// CHECK-DEF-NEXT: ret void
+//
+// CHECK-GNU-LABEL: define dso_local [1 x i64] @foo1
+// CHECK-GNU-SAME: ([1 x i64] noundef [[X_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT: [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT: store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-NEXT: [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-GNU-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT: [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-GNU-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT: store float [[X_REAL]], ptr [[RETVAL_REALP]], align 4
+// CHECK-GNU-NEXT: store float [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// CHECK-GNU-NEXT: [[TMP0:%.*]] = load [1 x i64], ptr [[RETVAL]], align 4
+// CHECK-GNU-NEXT: ret [1 x i64] [[TMP0]]
+//
+_Complex float foo1(_Complex float x) {
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo2
+// CHECK-DEF-SAME: (ptr noalias sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-DEF-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: store double [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT: store double [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// CHECK-DEF-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT: store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// CHECK-DEF-NEXT: store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// CHECK-DEF-NEXT: ret void
+//
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @foo2
+// CHECK-GNU-SAME: ([4 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT: entry:
+// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT: [[X:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT: store [4 x i32] [[X_COERCE]], ptr [[X]], align 8
+// CHECK-GNU-NEXT: [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-GNU-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT: [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-GNU-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT: store double [[X_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT: store double [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT: [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT: ret [4 x i32] [[TMP0]]
+//
+_Complex double foo2(_Complex double x) {
+ return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo3
+// CHECK-DEF-SAME: (ptr noalias sret({ ppc_fp128, ppc_fp128 }) align 16 [[AGG_RESULT:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT: entry:
+// CHECK-DEF-NEXT: [[X_REALP:%.*]] = getelementp...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
f79b66c to
ec05087
Compare
ec05087 to
6a209fb
Compare
|
ping! @chmeeedalf @nemanjai |
I know nothing about the complex ABI, so all I can say is the code looks okay from a structural point, can't say anything to the logic. |
| enum ComplexArgumentConventionKind { | ||
| CMPLX_Default, | ||
| CMPLX_OnStack, | ||
| CMPLX_OnGPR, // if ppc32 -fcomplex-ppc-gnu-abi |
There was a problem hiding this comment.
Nit: can we use In[GF]PR in the names as "in registers"/"on stack" are in common usage.
| 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, store Complex values in GPR instead of stack for PowerPC-32">, |
There was a problem hiding this comment.
Please use "Pass" instead of "Store" as the latter generally refers to storing values in memory and this actually affects parameter passing.
| std::unique_ptr<TargetCodeGenInfo> | ||
| createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI); | ||
| createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI, | ||
| unsigned RLen); |
There was a problem hiding this comment.
Please explain this change. It is not clear to me why we're adding this parameter. Are we adding support for 32-bit targets with 64-bit pointers? If so, please justify. Also, this is something that would belong in a separate patch.
There was a problem hiding this comment.
I was not sure about hardcoding Register Width(RLen) "32" for RLen here.. https://github.com/llvm/llvm-project/pull/77732/files#diff-05339beb4a6cf7efdfc537984255dd372dcf1edd2d00eabed25831da4d1d0a9fR276
So i used similiar approach like, how RISC-V does to calculate Register Width here.
PPC : https://github.com/llvm/llvm-project/pull/77732/files#diff-e724febedab9c1a2832bf2056d208ff02ddcb2e6f90b5a653afc9b19ac78a5d7R187
RISCV : https://github.com/llvm/llvm-project/pull/77732/files#diff-e724febedab9c1a2832bf2056d208ff02ddcb2e6f90b5a653afc9b19ac78a5d7L226
|
My review is not complete, I just submitted what I have so far so at least we can get started on answering the questions I have so far. |
| bool IsSoftFloat = | ||
| CodeGenOpts.FloatABI == "soft" || Target.hasFeature("spe"); | ||
| return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat); | ||
| unsigned RLen = Target.getPointerWidth(LangAS::Default); |
There was a problem hiding this comment.
the variable is used only once , do not need to introduce a new variable.
| bool IsSoftFloat = CodeGenOpts.FloatABI == "soft"; | ||
| return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat); | ||
| unsigned RLen = Target.getPointerWidth(LangAS::Default); | ||
| return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen); |
There was a problem hiding this comment.
the variable RLen is used only once , do not need to introduce a new variable.
| bool IsRetSmallStructInRegABI; | ||
| bool isComplexInRegABI; | ||
| // Size of GPR in bits | ||
| unsigned RLen; |
There was a problem hiding this comment.
RLen is too short to understand what R means , suggest to change to RegLen and RegisterLen. and Do we need to add RLen ? since the class is PPC32_SVR4_ABIInfo, Should the Register Len be default to 32 bit ?
|
|
||
| assert(Ty->isAnyComplexType()); | ||
| llvm::Type *ElemTy; | ||
| unsigned SizeRegs; |
There was a problem hiding this comment.
can you add a comment for SizeRegs what is stand for?
should it rename to ElemNum ?
| ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty, | ||
| uint64_t &TypeSize) const { | ||
|
|
||
| assert(Ty->isAnyComplexType()); |
There was a problem hiding this comment.
it looks the Ty pass in the function is only used for assert ?
| ElemTy = llvm::Type::getInt64Ty(getVMContext()); | ||
| SizeRegs = 1; | ||
| } else { | ||
| ElemTy = llvm::Type::getInt32Ty(getVMContext()); |
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
got it, thanks for explain. can you add your explain as comment ?
| if (TypeSize > RLen && TypeSize <= 2 * RLen) | ||
| ArgGPRsLeft -= 2; | ||
| else | ||
| ArgGPRsLeft--; |
There was a problem hiding this comment.
There is a potential bug here, you decrease the ArgGPRsLeft no mater whether ArgGPRsLeft >0;
and change the code here as
if(!Ty->isFloatingType() && TypeSize <= RLen * ArgGPRsLeft)
ArgGPRsLeft -= TypeSize / RLen;
amy-kwan
left a comment
There was a problem hiding this comment.
I think the braces can also be omitted on the conditions within the clang/* files.
| 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, store Complex values in GPR instead of stack for PowerPC-32">, |
There was a problem hiding this comment.
| DocBrief<"Follow the GNU ABI, store Complex values in GPR instead of stack for PowerPC-32">, | |
| DocBrief<"Follow the GNU ABI, pass Complex values in GPRs instead of the stack for PowerPC-32">, |
|
|
||
| /// If -fpcc-struct-return or -freg-struct-return is specified. | ||
| ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default) | ||
| /// If -fcomplex-ppc-gnu-abi for ppc32 |
There was a problem hiding this comment.
| /// If -fcomplex-ppc-gnu-abi for ppc32 | |
| /// If -fcomplex-ppc-gnu-abi is specified on ppc32. |
This sentence might read a bit better.
| bool isComplexInRegABI; | ||
| // Size of GPR in bits |
There was a problem hiding this comment.
| bool isComplexInRegABI; | |
| // Size of GPR in bits | |
| bool IsComplexInRegABI; | |
| // Size of GPR in bits. |
End sentence with a period, and capitalize the boolean variable for consistency.
| ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty, | ||
| uint64_t &TypeSize) const { | ||
|
|
||
| assert(Ty->isAnyComplexType()); |
There was a problem hiding this comment.
It would also be good if the assert has a message.
Additionally, for people who do not do assert builds, since Ty is only used for the assert, they may get an unused variable warning.
There was a problem hiding this comment.
if only use for the assert, suggest putting assert outside the function,
| return ABIArgInfo::getDirect(CoerceTy); | ||
| } | ||
| } | ||
| if (isComplexInRegABI && RetTy->isAnyComplexType()) { |
| : Context.LongLongTy)) | ||
| return getNaturalAlignIndirect(Ty); | ||
|
|
||
| return (isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty) |
There was a problem hiding this comment.
since most of the code is same as DefaultABIInfo::classifyArgumentType(QualType Ty) , I suggest rewrite the function PPC32_SVR4_ABIInfo::classifyArgumentType as
ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
int &ArgGPRsLeft) const {
....
special functionality code of the function here
.....
DefaultABIInfo::classifyArgumentType(Ty)
}
duplication code is not a good idea.
|
|
||
| enum ComplexArgumentConventionKind { | ||
| CMPLX_Default, | ||
| CMPLX_OnStack, |
There was a problem hiding this comment.
it looks you define the CMPLX_Default and CMPLX_OnStack , but never use them.
There was a problem hiding this comment.
This was for future reference, if any arch wants to use this.
There was a problem hiding this comment.
I don't think this needs to be a target-independent thing; this doesn't really generalize to other targets. I mean, maybe different targets have sort of similar schemes, but the same ABI compatibility issue shouldn't exist anywhere else. Maybe pass this as a target feature (ref getPPCTargetFeatures()).
|
|
||
| void computeInfo(CGFunctionInfo &FI) const override { | ||
|
|
||
| int ArgGPRsLeft = NumArgGPRs; |
There was a problem hiding this comment.
nit: move the definition to the closest first be used.
There was a problem hiding this comment.
Couldn't understand what you mean here..
There was a problem hiding this comment.
put int ArgGPRsLeft = NumArgGPRs; in front of
for (auto &I : FI.arguments()) I.info = classifyArgumentType(I.type, ArgGPRsLeft);
| @@ -0,0 +1,132 @@ | |||
| // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 | |||
|
|
|||
| // RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \ | |||
There was a problem hiding this comment.
do you really need -target-cpu pwr8 since the test case only check generated IR?
|
|
||
| // RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \ | ||
| // RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF | ||
| // RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 -fcomplex-ppc-gnu-abi \ |
| return handleComplex(Ty, TypeSize); | ||
| } | ||
|
|
||
| if (isAggregateTypeForABI(Ty)) { |
There was a problem hiding this comment.
it look that you only has Complex as parameter in your test case .for example _Complex float foo1(_Complex float x)
I think you need more test case with other parameters to test the functionality of function classifyArgumentType()
6a209fb to
a89cd20
Compare
|
ping! |
AaronBallman
left a comment
There was a problem hiding this comment.
I'm wondering why the GNU-compatible behavior is not on by default, especially when in GNU mode (e.g., -std=gnu17)?
Also, the changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the new functionality.
There was a problem hiding this comment.
| if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR) { | |
| GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi); | |
| } | |
| if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR) | |
| GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi); | |
There was a problem hiding this comment.
| if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) { | |
| Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR); | |
| } | |
| if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) | |
| Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR); |
There was a problem hiding this comment.
It'd be nice for this to check -### output to ensure that it's passing along the correct -cc1 option.
There was a problem hiding this comment.
We just use one single blank line as a separator.
473e188 to
2d4ae49
Compare
ATR-PASS-COMPLEX-IN-GPRS was missing in clang as shown in ABI doc : https://example61560.wordpress.com/wp-content/uploads/2016/11/powerpc_abi.pdf
@AaronBallman, can you guide me, in which section of ReleaseNotes.rst i should add info about new flag? |
I'm sorry but I still don't understand why it's not on by default in GNU mode. If the user is in GNU mode, they presumably are doing so because they want to be GNU-compatible, yes?
I'd add it under |
|
We were compiling simple testcase with complex parameters with clang and it was crashing because libraries was built using gcc. @AaronBallman, Reason for new flag was to enable this for other C standards as well. Currently we use c11 as standard in VxWorks toolchain, which has to work with gcc compiled libraries. |
2d4ae49 to
f463f28
Compare
Ah, perhaps I was unclear -- I'm not opposed to the flag existing so users can opt into/out of it as they want. I'm wondering why the flag needs to be specified when compiling with |
|
@AaronBallman, Yes you are right. I will work on it! |
…x arguments (llvm#77732) Fixes : llvm#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. Following up with this patch : https://reviews.llvm.org/D146942
869a686 to
d5c328d
Compare
|
I'm able to query if triple has gnu but can't query for -std=gnu..!! |
|
I think we should just use the GNU-compatible ABI by default. Unless there's some existing OS where clang is the platform compiler that wants to preserve the old ABI, but even if such a platform exists, I doubt there's anyone we can ask. |
|
cc @kernigh |
|
@efriedma-quic for the same reason, we created this switch. There was a debate regarding this conversation , reason being we concluded to creating the flag and not choosing GNU by default. |
|
ping! |
|
I am distracted by issue #133507 (clang-19 miscompiles itself for PPC32), so I can't check this pull request right now. My comment of August 29, 2023 had this example: gcc passed yes on the stack, but clang with the 2023 patch split yes between registers and the stack, so it was not yet compatible. Has the patch been fixed? I don't want clang to invent a 3rd complex ABI that is incompatible with both gcc and old clang.
For 32-bit PowerPC, I believe that clang can target Linux, NetBSD, FreeBSD, and OpenBSD. There are complex math functions like csqrt(3). I believe that Linux and NetBSD compile these functions with gcc, while FreeBSD and OpenBSD compile them with clang. For OpenBSD, talk to me. For FreeBSD, I don't know. I might want OpenBSD to switch back to gcc's ABI. OpenBSD 6.7 (in 2020) had changed its powerpc compiler from gcc to clang. This was before issue #56023 (in 2022) reported the ABI change. Back in 2020, nobody noticed OpenBSD's ABI change, but now, if I use gcc, I can't call csqrt(3). If clang switched to gcc's ABI, then gcc would work again. |
Yess, Works expected as gnu! |
efriedma-quic
left a comment
There was a problem hiding this comment.
Being incompatible with the platform compiler is the wrong default; we should be compatible by default. And the platform compiler is gcc for almost actively developed ppc32 targets, so we should just switch the default. Having a flag in addition to that seems fine if you think there's a chance people will need to control it.
|
|
||
| enum ComplexArgumentConventionKind { | ||
| CMPLX_Default, | ||
| CMPLX_OnStack, |
There was a problem hiding this comment.
I don't think this needs to be a target-independent thing; this doesn't really generalize to other targets. I mean, maybe different targets have sort of similar schemes, but the same ABI compatibility issue shouldn't exist anywhere else. Maybe pass this as a target feature (ref getPPCTargetFeatures()).
|
I'm interested in picking this up. Do we really think that this should be configurable though? Mirroring GCC sounds like clearly the right solution, if you need to be compatible with older clang you can just wrap the complex value in a struct. |
|
If we want to support a compatibility mode, we should probably just roll it into -fclang-abi-compat; I don't think it makes sense to carry it as an independent option. |
Uh oh!
There was an error while loading. Please reload this page.