https://github.com/Long5hot updated https://github.com/llvm/llvm-project/pull/77732
>From ac3e5526189f5d9e2abe739a21394a63f75567b5 Mon Sep 17 00:00:00 2001 From: Kishan Parmar <kparmar2...@gmail.com> Date: Fri, 15 Mar 2024 15:29:41 +0530 Subject: [PATCH] [clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments Fixes : https://github.com/llvm/llvm-project/issues/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 --- clang/include/clang/Basic/CodeGenOptions.def | 2 + clang/include/clang/Basic/CodeGenOptions.h | 6 + clang/include/clang/Driver/Options.td | 4 + clang/lib/CodeGen/Targets/PPC.cpp | 107 +++++++- clang/lib/Driver/ToolChains/Clang.cpp | 9 + clang/lib/Frontend/CompilerInvocation.cpp | 8 + .../CodeGen/PowerPC/ppc32-complex-gnu-abi.c | 255 ++++++++++++++++++ 7 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 340b08dd7e2a33..f4845e9e424c67 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -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. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 9469a424045bb0..1c9424f65623dd 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -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 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index a7e43b4d179a4d..2128b87abe8ea6 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2578,6 +2578,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], diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp index 00b04723f17dd2..32ebd3e1b4c2b5 100644 --- a/clang/lib/CodeGen/Targets/PPC.cpp +++ b/clang/lib/CodeGen/Targets/PPC.cpp @@ -330,22 +330,33 @@ namespace { class PPC32_SVR4_ABIInfo : public DefaultABIInfo { bool IsSoftFloatABI; bool IsRetSmallStructInRegABI; + bool IsComplexInRegABI; + // 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, - bool RetSmallStructInRegABI) + bool RetSmallStructInRegABI, bool ComplexInRegABI) : DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI), - IsRetSmallStructInRegABI(RetSmallStructInRegABI) {} + IsRetSmallStructInRegABI(RetSmallStructInRegABI), + IsComplexInRegABI(ComplexInRegABI) {} 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, @@ -355,9 +366,9 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo { class PPC32TargetCodeGenInfo : public TargetCodeGenInfo { public: PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI, - bool RetSmallStructInRegABI) + bool RetSmallStructInRegABI, bool ComplexInRegABI) : TargetCodeGenInfo(std::make_unique<PPC32_SVR4_ABIInfo>( - CGT, SoftFloatABI, RetSmallStructInRegABI)) {} + CGT, SoftFloatABI, RetSmallStructInRegABI, ComplexInRegABI)) {} static bool isStructReturnInRegABI(const llvm::Triple &Triple, const CodeGenOptions &Opts); @@ -396,12 +407,77 @@ 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 { + assert(ArgGPRsLeft >= 0 && "Arg GPR must be large or equal than zero"); + Ty = useFirstFieldIfTransparentUnion(Ty); + + ASTContext &Context = getContext(); + + uint64_t TypeSize = Context.getTypeSize(Ty); + + if (Ty->isAnyComplexType() && TypeSize <= RegLen * ArgGPRsLeft) { + assert(Ty->isAnyComplexType() && "Ty must be Complex type."); + if (IsComplexInRegABI) { + ArgGPRsLeft -= TypeSize / RegLen; + return handleComplex(TypeSize); + } else + ArgGPRsLeft--; + } + + if (ArgGPRsLeft) { + // Records with non-trivial destructors/copy-constructors should not be + // passed by value. + if (isAggregateTypeForABI(Ty)) + ArgGPRsLeft -= 1; + else if (!Ty->isFloatingType()) { + // For other premitive types. + if (TypeSize > RegLen && TypeSize <= ArgGPRsLeft * RegLen) + ArgGPRsLeft -= TypeSize / RegLen; + else + ArgGPRsLeft--; + } + } + + 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 @@ -420,6 +496,10 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const { return ABIArgInfo::getDirect(CoerceTy); } } + if (IsComplexInRegABI && RetTy->isAnyComplexType()) { + assert(RetTy->isAnyComplexType() && "RetTy must be Complex type."); + return handleComplex(Size); + } return DefaultABIInfo::classifyReturnType(RetTy); } @@ -431,11 +511,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; @@ -1036,8 +1117,10 @@ std::unique_ptr<TargetCodeGenInfo> CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI) { bool RetSmallStructInRegABI = PPC32TargetCodeGenInfo::isStructReturnInRegABI( CGM.getTriple(), CGM.getCodeGenOpts()); - return std::make_unique<PPC32TargetCodeGenInfo>(CGM.getTypes(), SoftFloatABI, - RetSmallStructInRegABI); + bool IsComplexInRegABI = (CGM.getCodeGenOpts().getComplexInRegABI() == + CodeGenOptions::CMPLX_InGPR); + return std::make_unique<PPC32TargetCodeGenInfo>( + CGM.getTypes(), SoftFloatABI, RetSmallStructInRegABI, IsComplexInRegABI); } std::unique_ptr<TargetCodeGenInfo> diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 3a7a1cf99c79ac..dddbe0d4230f9c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5523,6 +5523,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 451bdb9386f587..7bf12a8025a701 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -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); @@ -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) 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..b8e8193c8fda8c --- /dev/null +++ b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c @@ -0,0 +1,255 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 + +// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu \ +// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF +// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -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 dead_on_unwind noalias writable 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 dead_on_unwind noalias writable 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 dead_on_unwind noalias writable 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 [8 x i32] @foo3 +// CHECK-GNU-SAME: ([8 x i32] 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 [8 x i32] [[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 [8 x i32], ptr [[RETVAL]], align 16 +// CHECK-GNU-NEXT: ret [8 x i32] [[TMP0]] +// +_Complex long double foo3(_Complex long double x) { + return x; +} + +// CHECK-DEF-LABEL: define dso_local void @foo4 +// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ ppc_fp128, ppc_fp128 }) align 16 [[AGG_RESULT:%.*]], i32 noundef [[X:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[Y:%.*]]) #[[ATTR0]] { +// CHECK-DEF-NEXT: entry: +// CHECK-DEF-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-DEF-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-DEF-NEXT: [[Y_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[Y_REAL:%.*]] = load ppc_fp128, ptr [[Y_REALP]], align 16 +// CHECK-DEF-NEXT: [[Y_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 1 +// CHECK-DEF-NEXT: [[Y_IMAG:%.*]] = load ppc_fp128, ptr [[Y_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 [[Y_REAL]], ptr [[AGG_RESULT_REALP]], align 16 +// CHECK-DEF-NEXT: store ppc_fp128 [[Y_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 [8 x i32] @foo4 +// CHECK-GNU-SAME: (i32 noundef [[X:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[Y:%.*]]) #[[ATTR0]] { +// CHECK-GNU-NEXT: entry: +// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { ppc_fp128, ppc_fp128 }, align 16 +// CHECK-GNU-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-GNU-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-GNU-NEXT: [[Y_REALP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 0 +// CHECK-GNU-NEXT: [[Y_REAL:%.*]] = load ppc_fp128, ptr [[Y_REALP]], align 16 +// CHECK-GNU-NEXT: [[Y_IMAGP:%.*]] = getelementptr inbounds { ppc_fp128, ppc_fp128 }, ptr [[Y]], i32 0, i32 1 +// CHECK-GNU-NEXT: [[Y_IMAG:%.*]] = load ppc_fp128, ptr [[Y_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 [[Y_REAL]], ptr [[RETVAL_REALP]], align 16 +// CHECK-GNU-NEXT: store ppc_fp128 [[Y_IMAG]], ptr [[RETVAL_IMAGP]], align 16 +// CHECK-GNU-NEXT: [[TMP0:%.*]] = load [8 x i32], ptr [[RETVAL]], align 16 +// CHECK-GNU-NEXT: ret [8 x i32] [[TMP0]] +// +_Complex long double foo4(int x, _Complex long double y) +{ + return y; +} + +// CHECK-DEF-LABEL: define dso_local void @foo5 +// CHECK-DEF-SAME: (ptr dead_on_unwind noalias writable sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[W:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]], i32 noundef [[Y:%.*]], i64 noundef [[Z:%.*]]) #[[ATTR0]] { +// CHECK-DEF-NEXT: entry: +// CHECK-DEF-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4 +// CHECK-DEF-NEXT: [[Z_ADDR:%.*]] = alloca i64, align 8 +// CHECK-DEF-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4 +// CHECK-DEF-NEXT: store i64 [[Z]], ptr [[Z_ADDR]], align 8 +// CHECK-DEF-NEXT: [[W_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 0 +// CHECK-DEF-NEXT: [[W_REAL:%.*]] = load double, ptr [[W_REALP]], align 8 +// CHECK-DEF-NEXT: [[W_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 1 +// CHECK-DEF-NEXT: [[W_IMAG:%.*]] = load double, ptr [[W_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 [[W_REAL]], ptr [[AGG_RESULT_REALP]], align 8 +// CHECK-DEF-NEXT: store double [[W_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] @foo5 +// CHECK-GNU-SAME: ([4 x i32] noundef [[W_COERCE:%.*]], [1 x i64] noundef [[X_COERCE:%.*]], i32 noundef [[Y:%.*]], i64 noundef [[Z:%.*]]) #[[ATTR0]] { +// CHECK-GNU-NEXT: entry: +// CHECK-GNU-NEXT: [[RETVAL:%.*]] = alloca { double, double }, align 8 +// CHECK-GNU-NEXT: [[W:%.*]] = alloca { double, double }, align 8 +// CHECK-GNU-NEXT: [[X:%.*]] = alloca { float, float }, align 4 +// CHECK-GNU-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4 +// CHECK-GNU-NEXT: [[Z_ADDR:%.*]] = alloca i64, align 8 +// CHECK-GNU-NEXT: store [4 x i32] [[W_COERCE]], ptr [[W]], align 8 +// CHECK-GNU-NEXT: store [1 x i64] [[X_COERCE]], ptr [[X]], align 4 +// CHECK-GNU-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4 +// CHECK-GNU-NEXT: store i64 [[Z]], ptr [[Z_ADDR]], align 8 +// CHECK-GNU-NEXT: [[W_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 0 +// CHECK-GNU-NEXT: [[W_REAL:%.*]] = load double, ptr [[W_REALP]], align 8 +// CHECK-GNU-NEXT: [[W_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[W]], i32 0, i32 1 +// CHECK-GNU-NEXT: [[W_IMAG:%.*]] = load double, ptr [[W_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 [[W_REAL]], ptr [[RETVAL_REALP]], align 8 +// CHECK-GNU-NEXT: store double [[W_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 foo5(_Complex double w, _Complex float x, int y, long long z) +{ + return w; +} + +// CHECK-DEF-LABEL: define dso_local i32 @foo6 +// CHECK-DEF-SAME: (ptr noundef byval({ double, double }) align 8 [[W:%.*]], ptr noundef byval({ double, double }) align 8 [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0]] { +// CHECK-DEF-NEXT: entry: +// CHECK-DEF-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4 +// CHECK-DEF-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4 +// CHECK-DEF-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y_ADDR]], align 4 +// CHECK-DEF-NEXT: ret i32 [[TMP0]] +// +// CHECK-GNU-LABEL: define dso_local i32 @foo6 +// CHECK-GNU-SAME: ([4 x i32] noundef [[W_COERCE:%.*]], [4 x i32] noundef [[X_COERCE:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0]] { +// CHECK-GNU-NEXT: entry: +// CHECK-GNU-NEXT: [[W:%.*]] = alloca { double, double }, align 8 +// CHECK-GNU-NEXT: [[X:%.*]] = alloca { double, double }, align 8 +// CHECK-GNU-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4 +// CHECK-GNU-NEXT: store [4 x i32] [[W_COERCE]], ptr [[W]], align 8 +// CHECK-GNU-NEXT: store [4 x i32] [[X_COERCE]], ptr [[X]], align 8 +// CHECK-GNU-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4 +// CHECK-GNU-NEXT: [[TMP0:%.*]] = load i32, ptr [[Y_ADDR]], align 4 +// CHECK-GNU-NEXT: ret i32 [[TMP0]] +// +int foo6(_Complex double w, _Complex double x, int y) +{ + return y; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits