Skip to content

Commit 4ca2cad

Browse files
author
Justin Hibbits
committed
[PowerPC] Add clang -msvr4-struct-return for 32-bit ELF
Summary: Change the default ABI to be compatible with GCC. For 32-bit ELF targets other than Linux, Clang now returns small structs in registers r3/r4. This affects FreeBSD, NetBSD, OpenBSD. There is no change for 32-bit Linux, where Clang continues to return all structs in memory. Add clang options -maix-struct-return (to return structs in memory) and -msvr4-struct-return (to return structs in registers) to be compatible with gcc. These options are only for PPC32; reject them on PPC64 and other targets. The options are like -fpcc-struct-return and -freg-struct-return for X86_32, and use similar code. To actually return a struct in registers, coerce it to an integer of the same size. LLVM may optimize the code to remove unnecessary accesses to memory, and will return i32 in r3 or i64 in r3:r4. Fixes PR#40736 Patch by George Koehler! Reviewed By: jhibbits, nemanjai Differential Revision: https://reviews.llvm.org/D73290
1 parent a30e7ea commit 4ca2cad

File tree

7 files changed

+212
-10
lines changed

7 files changed

+212
-10
lines changed

Diff for: clang/docs/ClangCommandLineReference.rst

+10
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,11 @@ Enable MT ASE (MIPS only)
29732973

29742974
PowerPC
29752975
-------
2976+
.. option:: -maix-struct-return
2977+
2978+
Override the default ABI for 32-bit targets to return all structs in memory,
2979+
as in the Power 32-bit ABI for Linux (2011), and on AIX and Darwin.
2980+
29762981
.. option:: -maltivec, -mno-altivec
29772982

29782983
.. option:: -mcmpb, -mno-cmpb
@@ -3009,6 +3014,11 @@ PowerPC
30093014

30103015
.. option:: -mspe, -mno-spe
30113016

3017+
.. option:: -msvr4-struct-return
3018+
3019+
Override the default ABI for 32-bit targets to return small structs in
3020+
registers, as in the System V ABI (1995).
3021+
30123022
.. option:: -mvsx, -mno-vsx
30133023

30143024
WebAssembly

Diff for: clang/include/clang/Driver/Options.td

+6
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,12 @@ def mlongcall: Flag<["-"], "mlongcall">,
25102510
Group<m_ppc_Features_Group>;
25112511
def mno_longcall : Flag<["-"], "mno-longcall">,
25122512
Group<m_ppc_Features_Group>;
2513+
def maix_struct_return : Flag<["-"], "maix-struct-return">,
2514+
Group<m_Group>, Flags<[CC1Option]>,
2515+
HelpText<"Return all structs in memory (PPC32 only)">;
2516+
def msvr4_struct_return : Flag<["-"], "msvr4-struct-return">,
2517+
Group<m_Group>, Flags<[CC1Option]>,
2518+
HelpText<"Return small structs in registers (PPC32 only)">;
25132519

25142520
def mvx : Flag<["-"], "mvx">, Group<m_Group>;
25152521
def mno_vx : Flag<["-"], "mno-vx">, Group<m_Group>;

Diff for: clang/lib/CodeGen/TargetInfo.cpp

+75-7
Original file line numberDiff line numberDiff line change
@@ -4177,21 +4177,38 @@ namespace {
41774177
/// PPC32_SVR4_ABIInfo - The 32-bit PowerPC ELF (SVR4) ABI information.
41784178
class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
41794179
bool IsSoftFloatABI;
4180+
bool IsRetSmallStructInRegABI;
41804181

41814182
CharUnits getParamTypeAlignment(QualType Ty) const;
41824183

41834184
public:
4184-
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI)
4185-
: DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI) {}
4185+
PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
4186+
bool RetSmallStructInRegABI)
4187+
: DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI),
4188+
IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
4189+
4190+
ABIArgInfo classifyReturnType(QualType RetTy) const;
4191+
4192+
void computeInfo(CGFunctionInfo &FI) const override {
4193+
if (!getCXXABI().classifyReturnType(FI))
4194+
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
4195+
for (auto &I : FI.arguments())
4196+
I.info = classifyArgumentType(I.type);
4197+
}
41864198

41874199
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
41884200
QualType Ty) const override;
41894201
};
41904202

41914203
class PPC32TargetCodeGenInfo : public TargetCodeGenInfo {
41924204
public:
4193-
PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI)
4194-
: TargetCodeGenInfo(new PPC32_SVR4_ABIInfo(CGT, SoftFloatABI)) {}
4205+
PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI,
4206+
bool RetSmallStructInRegABI)
4207+
: TargetCodeGenInfo(new PPC32_SVR4_ABIInfo(CGT, SoftFloatABI,
4208+
RetSmallStructInRegABI)) {}
4209+
4210+
static bool isStructReturnInRegABI(const llvm::Triple &Triple,
4211+
const CodeGenOptions &Opts);
41954212

41964213
int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const override {
41974214
// This is recovered from gcc output.
@@ -4227,6 +4244,34 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
42274244
return CharUnits::fromQuantity(4);
42284245
}
42294246

4247+
ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
4248+
uint64_t Size;
4249+
4250+
// -msvr4-struct-return puts small aggregates in GPR3 and GPR4.
4251+
if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI &&
4252+
(Size = getContext().getTypeSize(RetTy)) <= 64) {
4253+
// System V ABI (1995), page 3-22, specified:
4254+
// > A structure or union whose size is less than or equal to 8 bytes
4255+
// > shall be returned in r3 and r4, as if it were first stored in the
4256+
// > 8-byte aligned memory area and then the low addressed word were
4257+
// > loaded into r3 and the high-addressed word into r4. Bits beyond
4258+
// > the last member of the structure or union are not defined.
4259+
//
4260+
// GCC for big-endian PPC32 inserts the pad before the first member,
4261+
// not "beyond the last member" of the struct. To stay compatible
4262+
// with GCC, we coerce the struct to an integer of the same size.
4263+
// LLVM will extend it and return i32 in r3, or i64 in r3:r4.
4264+
if (Size == 0)
4265+
return ABIArgInfo::getIgnore();
4266+
else {
4267+
llvm::Type *CoerceTy = llvm::Type::getIntNTy(getVMContext(), Size);
4268+
return ABIArgInfo::getDirect(CoerceTy);
4269+
}
4270+
}
4271+
4272+
return DefaultABIInfo::classifyReturnType(RetTy);
4273+
}
4274+
42304275
// TODO: this implementation is now likely redundant with
42314276
// DefaultABIInfo::EmitVAArg.
42324277
Address PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
@@ -4382,6 +4427,25 @@ Address PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
43824427
return Result;
43834428
}
43844429

4430+
bool PPC32TargetCodeGenInfo::isStructReturnInRegABI(
4431+
const llvm::Triple &Triple, const CodeGenOptions &Opts) {
4432+
assert(Triple.getArch() == llvm::Triple::ppc);
4433+
4434+
switch (Opts.getStructReturnConvention()) {
4435+
case CodeGenOptions::SRCK_Default:
4436+
break;
4437+
case CodeGenOptions::SRCK_OnStack: // -maix-struct-return
4438+
return false;
4439+
case CodeGenOptions::SRCK_InRegs: // -msvr4-struct-return
4440+
return true;
4441+
}
4442+
4443+
if (Triple.isOSBinFormatELF() && !Triple.isOSLinux())
4444+
return true;
4445+
4446+
return false;
4447+
}
4448+
43854449
bool
43864450
PPC32TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
43874451
llvm::Value *Address) const {
@@ -10264,10 +10328,14 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
1026410328
return SetCGInfo(new ARMTargetCodeGenInfo(Types, Kind));
1026510329
}
1026610330

10267-
case llvm::Triple::ppc:
10331+
case llvm::Triple::ppc: {
10332+
bool IsSoftFloat =
10333+
CodeGenOpts.FloatABI == "soft" || getTarget().hasFeature("spe");
10334+
bool RetSmallStructInRegABI =
10335+
PPC32TargetCodeGenInfo::isStructReturnInRegABI(Triple, CodeGenOpts);
1026810336
return SetCGInfo(
10269-
new PPC32TargetCodeGenInfo(Types, CodeGenOpts.FloatABI == "soft" ||
10270-
getTarget().hasFeature("spe")));
10337+
new PPC32TargetCodeGenInfo(Types, IsSoftFloat, RetSmallStructInRegABI));
10338+
}
1027110339
case llvm::Triple::ppc64:
1027210340
if (Triple.isOSBinFormatELF()) {
1027310341
PPC64_SVR4_ABIInfo::ABIKind Kind = PPC64_SVR4_ABIInfo::ELFv1;

Diff for: clang/lib/Driver/ToolChains/Clang.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -4535,6 +4535,19 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
45354535
CmdArgs.push_back(A->getValue());
45364536
}
45374537

4538+
if (Arg *A = Args.getLastArg(options::OPT_maix_struct_return,
4539+
options::OPT_msvr4_struct_return)) {
4540+
if (TC.getArch() != llvm::Triple::ppc) {
4541+
D.Diag(diag::err_drv_unsupported_opt_for_target)
4542+
<< A->getSpelling() << RawTriple.str();
4543+
} else if (A->getOption().matches(options::OPT_maix_struct_return)) {
4544+
CmdArgs.push_back("-maix-struct-return");
4545+
} else {
4546+
assert(A->getOption().matches(options::OPT_msvr4_struct_return));
4547+
CmdArgs.push_back("-msvr4-struct-return");
4548+
}
4549+
}
4550+
45384551
if (Arg *A = Args.getLastArg(options::OPT_fpcc_struct_return,
45394552
options::OPT_freg_struct_return)) {
45404553
if (TC.getArch() != llvm::Triple::x86) {

Diff for: clang/lib/Frontend/CompilerInvocation.cpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -1297,11 +1297,18 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
12971297
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
12981298
}
12991299

1300-
if (Arg *A = Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return)) {
1301-
if (A->getOption().matches(OPT_fpcc_struct_return)) {
1300+
// X86_32 has -fppc-struct-return and -freg-struct-return.
1301+
// PPC32 has -maix-struct-return and -msvr4-struct-return.
1302+
if (Arg *A =
1303+
Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return,
1304+
OPT_maix_struct_return, OPT_msvr4_struct_return)) {
1305+
const Option &O = A->getOption();
1306+
if (O.matches(OPT_fpcc_struct_return) ||
1307+
O.matches(OPT_maix_struct_return)) {
13021308
Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack);
13031309
} else {
1304-
assert(A->getOption().matches(OPT_freg_struct_return));
1310+
assert(O.matches(OPT_freg_struct_return) ||
1311+
O.matches(OPT_msvr4_struct_return));
13051312
Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs);
13061313
}
13071314
}

Diff for: clang/test/CodeGen/ppc32-struct-return.c

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// REQUIRES: powerpc-registered-target
2+
// RUN: %clang_cc1 -triple powerpc-unknown-freebsd \
3+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4
4+
// RUN: %clang_cc1 -triple powerpc-unknown-linux \
5+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX
6+
// RUN: %clang_cc1 -triple powerpc-unknown-linux -maix-struct-return \
7+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX
8+
// RUN: %clang_cc1 -triple powerpc-unknown-linux -msvr4-struct-return \
9+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4
10+
// RUN: %clang_cc1 -triple powerpc-unknown-netbsd \
11+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4
12+
// RUN: %clang_cc1 -triple powerpc-unknown-openbsd \
13+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4
14+
// RUN: %clang_cc1 -triple powerpc-unknown-openbsd -maix-struct-return \
15+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-AIX
16+
// RUN: %clang_cc1 -triple powerpc-unknown-openbsd -msvr4-struct-return \
17+
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-SVR4
18+
19+
typedef struct {
20+
} Zero;
21+
typedef struct {
22+
char c;
23+
} One;
24+
typedef struct {
25+
short s;
26+
} Two;
27+
typedef struct {
28+
char c[3];
29+
} Three;
30+
typedef struct {
31+
float f;
32+
} Four; // svr4 to return i32, not float
33+
typedef struct {
34+
char c[5];
35+
} Five;
36+
typedef struct {
37+
short s[3];
38+
} Six;
39+
typedef struct {
40+
char c[7];
41+
} Seven;
42+
typedef struct {
43+
int i;
44+
char c;
45+
} Eight; // padded for alignment
46+
typedef struct {
47+
char c[9];
48+
} Nine;
49+
50+
// CHECK-AIX-LABEL: define void @ret0(%struct.Zero* noalias sret {{[^,]*}})
51+
// CHECK-SVR4-LABEL: define void @ret0()
52+
Zero ret0(void) { return (Zero){}; }
53+
54+
// CHECK-AIX-LABEL: define void @ret1(%struct.One* noalias sret {{[^,]*}})
55+
// CHECK-SVR4-LABEL: define i8 @ret1()
56+
One ret1(void) { return (One){'a'}; }
57+
58+
// CHECK-AIX-LABEL: define void @ret2(%struct.Two* noalias sret {{[^,]*}})
59+
// CHECK-SVR4-LABEL: define i16 @ret2()
60+
Two ret2(void) { return (Two){123}; }
61+
62+
// CHECK-AIX-LABEL: define void @ret3(%struct.Three* noalias sret {{[^,]*}})
63+
// CHECK-SVR4-LABEL: define i24 @ret3()
64+
Three ret3(void) { return (Three){"abc"}; }
65+
66+
// CHECK-AIX-LABEL: define void @ret4(%struct.Four* noalias sret {{[^,]*}})
67+
// CHECK-SVR4-LABEL: define i32 @ret4()
68+
Four ret4(void) { return (Four){0.4}; }
69+
70+
// CHECK-AIX-LABEL: define void @ret5(%struct.Five* noalias sret {{[^,]*}})
71+
// CHECK-SVR4-LABEL: define i40 @ret5()
72+
Five ret5(void) { return (Five){"abcde"}; }
73+
74+
// CHECK-AIX-LABEL: define void @ret6(%struct.Six* noalias sret {{[^,]*}})
75+
// CHECK-SVR4-LABEL: define i48 @ret6()
76+
Six ret6(void) { return (Six){12, 34, 56}; }
77+
78+
// CHECK-AIX-LABEL: define void @ret7(%struct.Seven* noalias sret {{[^,]*}})
79+
// CHECK-SVR4-LABEL: define i56 @ret7()
80+
Seven ret7(void) { return (Seven){"abcdefg"}; }
81+
82+
// CHECK-AIX-LABEL: define void @ret8(%struct.Eight* noalias sret {{[^,]*}})
83+
// CHECK-SVR4-LABEL: define i64 @ret8()
84+
Eight ret8(void) { return (Eight){123, 'a'}; }
85+
86+
// CHECK-AIX-LABEL: define void @ret9(%struct.Nine* noalias sret {{[^,]*}})
87+
// CHECK-SVR4-LABEL: define void @ret9(%struct.Nine* noalias sret {{[^,]*}})
88+
Nine ret9(void) { return (Nine){"abcdefghi"}; }

Diff for: clang/test/Driver/ppc-unsupported.c

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// REQUIRES: powerpc-registered-target
2+
// RUN: not %clang -target powerpc64-unknown-freebsd -maix-struct-return \
3+
// RUN: -c %s 2>&1 | FileCheck %s
4+
// RUN: not %clang -target powerpc64-unknown-freebsd -msvr4-struct-return \
5+
// RUN: -c %s 2>&1 | FileCheck %s
6+
// RUN: not %clang -target powerpc64le-unknown-linux -maix-struct-return \
7+
// RUN: -c %s 2>&1 | FileCheck %s
8+
// RUN: not %clang -target powerpc64le-unknown-linux -msvr4-struct-return \
9+
// RUN: -c %s 2>&1 | FileCheck %s
10+
// CHECK: unsupported option

0 commit comments

Comments
 (0)