diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -84,6 +84,13 @@ SRCK_InRegs // Small structs in registers (-freg-struct-return). }; + enum ComplexArgumentConventionKind { + CMPLX_Default, + CMPLX_OnStack, + CMPLX_OnGPR, + CMPLX_OnFPR + }; + enum ProfileInstrKind { ProfileNone, // Profile instrumentation is turned off. ProfileClangInstr, // Clang instrumentation to generate execution counts diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -229,6 +229,9 @@ /// 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. CODEGENOPT(StructPathTBAA , 1, 0) ///< Whether or not to use struct-path TBAA. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2163,6 +2163,10 @@ 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, Flags<[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( - CGT, SoftFloatABI, RetSmallStructInRegABI)) {} + CGT, SoftFloatABI, RetSmallStructInRegABI, + ComplexInRegABI)) {} static bool isStructReturnInRegABI(const llvm::Triple &Triple, const CodeGenOptions &Opts); @@ -337,6 +344,32 @@ return CharUnits::fromQuantity(4); } +ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty) const{ + + assert(Ty->isAnyComplexType()); + uint64_t TypeSize = getContext().getTypeSize(Ty); + llvm::Type* ElemTy; + unsigned SizeRegs; + + if (TypeSize <= 64) { + ElemTy = llvm::Type::getInt32Ty(getVMContext()); + SizeRegs = (TypeSize + 31) / 32; + } else { + ElemTy = llvm::Type::getInt64Ty(getVMContext()); + SizeRegs = (TypeSize + 63) / 64; + } + return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, SizeRegs)); +} + +ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty) const{ + Ty = useFirstFieldIfTransparentUnion(Ty); + + if (isComplexInRegABI && Ty->isAnyComplexType()) { + return handleComplex(Ty); + } + return DefaultABIInfo::classifyArgumentType(Ty); +} + ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const { uint64_t Size; @@ -361,6 +394,9 @@ return ABIArgInfo::getDirect(CoerceTy); } } + if(isComplexInRegABI && RetTy->isAnyComplexType()) { + return handleComplex(RetTy); + } return DefaultABIInfo::classifyReturnType(RetTy); } @@ -956,8 +992,10 @@ CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI) { bool RetSmallStructInRegABI = PPC32TargetCodeGenInfo::isStructReturnInRegABI( CGM.getTriple(), CGM.getCodeGenOpts()); + bool isComplexInRegABI = (CGM.getCodeGenOpts().getComplexInRegABI() == CodeGenOptions::CMPLX_OnGPR); return std::make_unique(CGM.getTypes(), SoftFloatABI, - RetSmallStructInRegABI); + RetSmallStructInRegABI, + isComplexInRegABI); } std::unique_ptr diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5431,6 +5431,15 @@ } } + 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)) CmdArgs.push_back("-fdefault-calling-conv=stdcall"); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1540,6 +1540,10 @@ 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); @@ -1920,6 +1924,10 @@ } } + 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 --- /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 powerpcle-unknown-linux-gnu -target-cpu pwr8 \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF +// RUN: %clang_cc1 -triple powerpcle-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 [2 x i32] @foo1 +// CHECK-GNU-SAME: ([2 x i32] 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 [2 x i32] [[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 [2 x i32], ptr [[RETVAL]], align 4 +// CHECK-GNU-NEXT: ret [2 x i32] [[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 [2 x i64] @foo2 +// CHECK-GNU-SAME: ([2 x i64] 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 [2 x i64] [[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 [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-GNU-NEXT: ret [2 x i64] [[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:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16 +// CHECK-DEF-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1 +// CHECK-DEF-NEXT: [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16 +// CHECK-DEF-NEXT: [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-DEF-NEXT: store ppc_fp128 [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 16 +// CHECK-DEF-NEXT: store ppc_fp128 [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 16 +// CHECK-DEF-NEXT: [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[AGG_RESULT_REAL:%.*]] = load ppc_fp128, ptr [[AGG_RESULT_REALP1]], align 16 +// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-DEF-NEXT: [[AGG_RESULT_IMAG:%.*]] = load ppc_fp128, ptr [[AGG_RESULT_IMAGP2]], align 16 +// CHECK-DEF-NEXT: [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-DEF-NEXT: store ppc_fp128 [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 16 +// CHECK-DEF-NEXT: store ppc_fp128 [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 16 +// CHECK-DEF-NEXT: ret void +// +// CHECK-GNU-LABEL: define dso_local [4 x i64] @foo3 +// CHECK-GNU-SAME: ([4 x i64] noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-GNU-NEXT: entry: +// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16 +// CHECK-GNU-NEXT: [[X:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16 +// CHECK-GNU-NEXT: store [4 x i64] [[X_COERCE]], ptr [[X]], align 16 +// CHECK-GNU-NEXT: [[X_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 0 +// CHECK-GNU-NEXT: [[X_REAL:%.*]] = load ppc_fp128, ptr [[X_REALP]], align 16 +// CHECK-GNU-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[X]], i32 0, i32 1 +// CHECK-GNU-NEXT: [[X_IMAG:%.*]] = load ppc_fp128, ptr [[X_IMAGP]], align 16 +// CHECK-GNU-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 0 +// CHECK-GNU-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[RETVAL]], i32 0, i32 1 +// CHECK-GNU-NEXT: store ppc_fp128 [[X_REAL]], ptr [[RETVAL_REALP]], align 16 +// CHECK-GNU-NEXT: store ppc_fp128 [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 16 +// CHECK-GNU-NEXT: [[TMP0:%.*]] = load [4 x i64], ptr [[RETVAL]], align 16 +// CHECK-GNU-NEXT: ret [4 x i64] [[TMP0]] +// +_Complex long double foo3(_Complex long double x) { + return x; +}