diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2973,6 +2973,11 @@ PowerPC ------- +.. option:: -maix-struct-return + +Override the default ABI for 32-bit targets to return all structs in memory, +as in the Power 32-bit ABI for Linux (2011), and on AIX and Darwin. + .. option:: -maltivec, -mno-altivec .. option:: -mcmpb, -mno-cmpb @@ -3009,6 +3014,11 @@ .. option:: -mspe, -mno-spe +.. option:: -msvr4-struct-return + +Override the default ABI for 32-bit targets to return small structs in +registers, as in the System V ABI (1995). + .. option:: -mvsx, -mno-vsx WebAssembly 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 @@ -2510,6 +2510,12 @@ Group; def mno_longcall : Flag<["-"], "mno-longcall">, Group; +def maix_struct_return : Flag<["-"], "maix-struct-return">, + Group, Flags<[CC1Option]>, + HelpText<"Return all structs in memory (PPC32 only)">; +def msvr4_struct_return : Flag<["-"], "msvr4-struct-return">, + Group, Flags<[CC1Option]>, + HelpText<"Return small structs in registers (PPC32 only)">; def mvx : Flag<["-"], "mvx">, Group; def mno_vx : Flag<["-"], "mno-vx">, Group; diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -4177,12 +4177,24 @@ /// PPC32_SVR4_ABIInfo - The 32-bit PowerPC ELF (SVR4) ABI information. class PPC32_SVR4_ABIInfo : public DefaultABIInfo { bool IsSoftFloatABI; + bool IsRetSmallStructInRegABI; CharUnits getParamTypeAlignment(QualType Ty) const; public: - PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI) - : DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI) {} + PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI, + bool RetSmallStructInRegABI) + : DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI), + IsRetSmallStructInRegABI(RetSmallStructInRegABI) {} + + ABIArgInfo classifyReturnType(QualType RetTy) const; + + void computeInfo(CGFunctionInfo &FI) const override { + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + for (auto &I : FI.arguments()) + I.info = classifyArgumentType(I.type); + } Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty) const override; @@ -4190,8 +4202,13 @@ class PPC32TargetCodeGenInfo : public TargetCodeGenInfo { public: - PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI) - : TargetCodeGenInfo(new PPC32_SVR4_ABIInfo(CGT, SoftFloatABI)) {} + PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI, + bool RetSmallStructInRegABI) + : TargetCodeGenInfo(new PPC32_SVR4_ABIInfo(CGT, SoftFloatABI, + RetSmallStructInRegABI)) {} + + static bool isStructReturnInRegABI(const llvm::Triple &Triple, + const CodeGenOptions &Opts); int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const override { // This is recovered from gcc output. @@ -4227,6 +4244,34 @@ return CharUnits::fromQuantity(4); } +ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const { + uint64_t Size; + + // -msvr4-struct-return puts small aggregates in GPR3 and GPR4. + if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI && + (Size = getContext().getTypeSize(RetTy)) <= 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 + // > 8-byte aligned memory area and then the low addressed word were + // > loaded into r3 and the high-addressed word into r4. Bits beyond + // > the last member of the structure or union are not defined. + // + // GCC for big-endian PPC32 inserts the pad before the first member, + // not "beyond the last member" of the struct. To stay compatible + // with GCC, we coerce the struct to an integer of the same size. + // LLVM will extend it and return i32 in r3, or i64 in r3:r4. + if (Size == 0) + return ABIArgInfo::getIgnore(); + else { + llvm::Type *CoerceTy = llvm::Type::getIntNTy(getVMContext(), Size); + return ABIArgInfo::getDirect(CoerceTy); + } + } + + return DefaultABIInfo::classifyReturnType(RetTy); +} + // TODO: this implementation is now likely redundant with // DefaultABIInfo::EmitVAArg. Address PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList, @@ -4382,6 +4427,25 @@ return Result; } +bool PPC32TargetCodeGenInfo::isStructReturnInRegABI( + const llvm::Triple &Triple, const CodeGenOptions &Opts) { + assert(Triple.getArch() == llvm::Triple::ppc); + + switch (Opts.getStructReturnConvention()) { + case CodeGenOptions::SRCK_Default: + break; + case CodeGenOptions::SRCK_OnStack: // -maix-struct-return + return false; + case CodeGenOptions::SRCK_InRegs: // -msvr4-struct-return + return true; + } + + if (Triple.isOSBinFormatELF() && !Triple.isOSLinux()) + return true; + + return false; +} + bool PPC32TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, llvm::Value *Address) const { @@ -10264,10 +10328,14 @@ return SetCGInfo(new ARMTargetCodeGenInfo(Types, Kind)); } - case llvm::Triple::ppc: + case llvm::Triple::ppc: { + bool IsSoftFloat = + CodeGenOpts.FloatABI == "soft" || getTarget().hasFeature("spe"); + bool RetSmallStructInRegABI = + PPC32TargetCodeGenInfo::isStructReturnInRegABI(Triple, CodeGenOpts); return SetCGInfo( - new PPC32TargetCodeGenInfo(Types, CodeGenOpts.FloatABI == "soft" || - getTarget().hasFeature("spe"))); + new PPC32TargetCodeGenInfo(Types, IsSoftFloat, RetSmallStructInRegABI)); + } case llvm::Triple::ppc64: if (Triple.isOSBinFormatELF()) { PPC64_SVR4_ABIInfo::ABIKind Kind = PPC64_SVR4_ABIInfo::ELFv1; 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 @@ -4535,6 +4535,19 @@ CmdArgs.push_back(A->getValue()); } + if (Arg *A = Args.getLastArg(options::OPT_maix_struct_return, + options::OPT_msvr4_struct_return)) { + if (TC.getArch() != llvm::Triple::ppc) { + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getSpelling() << RawTriple.str(); + } else if (A->getOption().matches(options::OPT_maix_struct_return)) { + CmdArgs.push_back("-maix-struct-return"); + } else { + assert(A->getOption().matches(options::OPT_msvr4_struct_return)); + CmdArgs.push_back("-msvr4-struct-return"); + } + } + if (Arg *A = Args.getLastArg(options::OPT_fpcc_struct_return, options::OPT_freg_struct_return)) { if (TC.getArch() != llvm::Triple::x86) { 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 @@ -1297,11 +1297,18 @@ Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; } - if (Arg *A = Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return)) { - if (A->getOption().matches(OPT_fpcc_struct_return)) { + // X86_32 has -fppc-struct-return and -freg-struct-return. + // PPC32 has -maix-struct-return and -msvr4-struct-return. + if (Arg *A = + Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return, + OPT_maix_struct_return, OPT_msvr4_struct_return)) { + const Option &O = A->getOption(); + if (O.matches(OPT_fpcc_struct_return) || + O.matches(OPT_maix_struct_return)) { Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack); } else { - assert(A->getOption().matches(OPT_freg_struct_return)); + assert(O.matches(OPT_freg_struct_return) || + O.matches(OPT_msvr4_struct_return)); Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs); } } diff --git a/clang/test/CodeGen/ppc32-struct-return.c b/clang/test/CodeGen/ppc32-struct-return.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/ppc32-struct-return.c @@ -0,0 +1,88 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_cc1 -triple powerpc-unknown-freebsd \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4 +// RUN: %clang_cc1 -triple powerpc-unknown-linux \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX +// RUN: %clang_cc1 -triple powerpc-unknown-linux -maix-struct-return \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX +// RUN: %clang_cc1 -triple powerpc-unknown-linux -msvr4-struct-return \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4 +// RUN: %clang_cc1 -triple powerpc-unknown-netbsd \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4 +// RUN: %clang_cc1 -triple powerpc-unknown-openbsd \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4 +// RUN: %clang_cc1 -triple powerpc-unknown-openbsd -maix-struct-return \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX +// RUN: %clang_cc1 -triple powerpc-unknown-openbsd -msvr4-struct-return \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4 + +typedef struct { +} Zero; +typedef struct { + char c; +} One; +typedef struct { + short s; +} Two; +typedef struct { + char c[3]; +} Three; +typedef struct { + float f; +} Four; // svr4 to return i32, not float +typedef struct { + char c[5]; +} Five; +typedef struct { + short s[3]; +} Six; +typedef struct { + char c[7]; +} Seven; +typedef struct { + int i; + char c; +} Eight; // padded for alignment +typedef struct { + char c[9]; +} Nine; + +// CHECK-AIX-LABEL: define void @ret0(%struct.Zero* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define void @ret0() +Zero ret0(void) { return (Zero){}; } + +// CHECK-AIX-LABEL: define void @ret1(%struct.One* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i8 @ret1() +One ret1(void) { return (One){'a'}; } + +// CHECK-AIX-LABEL: define void @ret2(%struct.Two* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i16 @ret2() +Two ret2(void) { return (Two){123}; } + +// CHECK-AIX-LABEL: define void @ret3(%struct.Three* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i24 @ret3() +Three ret3(void) { return (Three){"abc"}; } + +// CHECK-AIX-LABEL: define void @ret4(%struct.Four* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i32 @ret4() +Four ret4(void) { return (Four){0.4}; } + +// CHECK-AIX-LABEL: define void @ret5(%struct.Five* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i40 @ret5() +Five ret5(void) { return (Five){"abcde"}; } + +// CHECK-AIX-LABEL: define void @ret6(%struct.Six* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i48 @ret6() +Six ret6(void) { return (Six){12, 34, 56}; } + +// CHECK-AIX-LABEL: define void @ret7(%struct.Seven* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i56 @ret7() +Seven ret7(void) { return (Seven){"abcdefg"}; } + +// CHECK-AIX-LABEL: define void @ret8(%struct.Eight* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define i64 @ret8() +Eight ret8(void) { return (Eight){123, 'a'}; } + +// CHECK-AIX-LABEL: define void @ret9(%struct.Nine* noalias sret {{[^,]*}}) +// CHECK-SVR4-LABEL: define void @ret9(%struct.Nine* noalias sret {{[^,]*}}) +Nine ret9(void) { return (Nine){"abcdefghi"}; } diff --git a/clang/test/Driver/ppc-unsupported.c b/clang/test/Driver/ppc-unsupported.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/ppc-unsupported.c @@ -0,0 +1,10 @@ +// REQUIRES: powerpc-registered-target +// RUN: not %clang -target powerpc64-unknown-freebsd -maix-struct-return \ +// RUN: -c %s 2>&1 | FileCheck %s +// RUN: not %clang -target powerpc64-unknown-freebsd -msvr4-struct-return \ +// RUN: -c %s 2>&1 | FileCheck %s +// RUN: not %clang -target powerpc64le-unknown-linux -maix-struct-return \ +// RUN: -c %s 2>&1 | FileCheck %s +// RUN: not %clang -target powerpc64le-unknown-linux -msvr4-struct-return \ +// RUN: -c %s 2>&1 | FileCheck %s +// CHECK: unsupported option