Skip to content

Commit

Permalink
[COFF, ARM64] Decide when to mark struct returns as SRet
Browse files Browse the repository at this point in the history
Summary:
Refer the MS ARM64 ABI Convention for the behavior for struct returns:
https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions#return-values

Reviewers: mstorsjo, compnerd, rnk, javed.absar, yinma, efriedma

Reviewed By: rnk, efriedma

Subscribers: haripul, TomTan, yinma, efriedma, kristof.beyls, chrib, llvm-commits

Differential Revision: https://reviews.llvm.org/D49464

llvm-svn: 338050
  • Loading branch information
Mandeep Singh Grang committed Jul 26, 2018
1 parent 3bdd600 commit 2a15310
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
16 changes: 14 additions & 2 deletions clang/include/clang/CodeGen/CGFunctionInfo.h
Expand Up @@ -96,6 +96,7 @@ class ABIArgInfo {
bool InReg : 1; // isDirect() || isExtend() || isIndirect()
bool CanBeFlattened: 1; // isDirect()
bool SignExt : 1; // isExtend()
bool SuppressSRet : 1; // isIndirect()

bool canHavePaddingType() const {
return isDirect() || isExtend() || isIndirect() || isExpand();
Expand All @@ -111,13 +112,14 @@ class ABIArgInfo {
}

ABIArgInfo(Kind K)
: TheKind(K), PaddingInReg(false), InReg(false) {
: TheKind(K), PaddingInReg(false), InReg(false), SuppressSRet(false) {
}

public:
ABIArgInfo()
: TypeData(nullptr), PaddingType(nullptr), DirectOffset(0),
TheKind(Direct), PaddingInReg(false), InReg(false) {}
TheKind(Direct), PaddingInReg(false), InReg(false),
SuppressSRet(false) {}

static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0,
llvm::Type *Padding = nullptr,
Expand Down Expand Up @@ -406,6 +408,16 @@ class ABIArgInfo {
CanBeFlattened = Flatten;
}

bool getSuppressSRet() const {
assert(isIndirect() && "Invalid kind!");
return SuppressSRet;
}

void setSuppressSRet(bool Suppress) {
assert(isIndirect() && "Invalid kind!");
SuppressSRet = Suppress;
}

void dump() const;
};

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Expand Up @@ -1988,7 +1988,8 @@ void CodeGenModule::ConstructAttributeList(
// Attach attributes to sret.
if (IRFunctionArgs.hasSRetArg()) {
llvm::AttrBuilder SRETAttrs;
SRETAttrs.addAttribute(llvm::Attribute::StructRet);
if (!RetAI.getSuppressSRet())
SRETAttrs.addAttribute(llvm::Attribute::StructRet);
hasUsedSRet = true;
if (RetAI.getInReg())
SRETAttrs.addAttribute(llvm::Attribute::InReg);
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Expand Up @@ -1060,10 +1060,22 @@ bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
// the second parameter.
FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false);
FI.getReturnInfo().setSRetAfterThis(FI.isInstanceMethod());

// aarch64-windows requires that instance methods use X1 for the return
// address. So for aarch64-windows we do not mark the
// return as SRet.
FI.getReturnInfo().setSuppressSRet(CGM.getTarget().getTriple().getArch() ==
llvm::Triple::aarch64);
return true;
} else if (!RD->isPOD()) {
// If it's a free function, non-POD types are returned indirectly.
FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false);

// aarch64-windows requires that non-POD, non-instance returns use X0 for
// the return address. So for aarch64-windows we do not mark the return as
// SRet.
FI.getReturnInfo().setSuppressSRet(CGM.getTarget().getTriple().getArch() ==
llvm::Triple::aarch64);
return true;
}

Expand Down
25 changes: 25 additions & 0 deletions clang/test/CodeGen/arm64-microsoft-arguments.cpp
@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -triple aarch64-windows -ffreestanding -emit-llvm -O0 \
// RUN: -x c++ -o - %s | FileCheck %s

struct pod { int a, b, c, d, e; };

struct non_pod {
int a;
non_pod() {}
};

struct pod s;
struct non_pod t;

struct pod bar() { return s; }
struct non_pod foo() { return t; }
// CHECK: define {{.*}} void @{{.*}}bar{{.*}}(%struct.pod* noalias sret %agg.result)
// CHECK: define {{.*}} void @{{.*}}foo{{.*}}(%struct.non_pod* noalias %agg.result)


// Check instance methods.
struct pod2 { int x; };
struct Baz { pod2 baz(); };

int qux() { return Baz().baz().x; }
// CHECK: declare {{.*}} void @{{.*}}baz@Baz{{.*}}(%struct.Baz*, %struct.pod2*)

0 comments on commit 2a15310

Please sign in to comment.