Set SRet flags properly in '-cxx-abi microsoft'.
Also, - abstract out the indirect/in memory/in registers decisions into the CGCXXABI - fix handling of empty struct arguments for '-cxx-abi microsoft' - add/fix tests llvm-svn: 179681
This commit is contained in:
parent
19a41f161b
commit
8fe501dc0f
|
@ -101,6 +101,27 @@ public:
|
|||
/// kinds that the ABI says returns 'this'.
|
||||
virtual bool HasThisReturn(GlobalDecl GD) const { return false; }
|
||||
|
||||
/// Returns true if the given record type should be returned indirectly.
|
||||
virtual bool isReturnTypeIndirect(const CXXRecordDecl *RD) const = 0;
|
||||
|
||||
/// Specify how one should pass an argument of a record type.
|
||||
enum RecordArgABI {
|
||||
/// Pass it using the normal C aggregate rules for the ABI, potentially
|
||||
/// introducing extra copies and passing some or all of it in registers.
|
||||
RAA_Default = 0,
|
||||
|
||||
/// Pass it on the stack using its defined layout. The argument must be
|
||||
/// evaluated directly into the correct stack position in the arguments area,
|
||||
/// and the call machinery must not move it or introduce extra copies.
|
||||
RAA_DirectInMemory,
|
||||
|
||||
/// Pass it as a pointer to temporary memory.
|
||||
RAA_Indirect
|
||||
};
|
||||
|
||||
/// Returns how an argument of the given record type should be passed.
|
||||
virtual RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const = 0;
|
||||
|
||||
/// Find the LLVM type used to represent the given member pointer
|
||||
/// type.
|
||||
virtual llvm::Type *
|
||||
|
|
|
@ -41,6 +41,20 @@ public:
|
|||
ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) :
|
||||
CGCXXABI(CGM), IsARM(IsARM) { }
|
||||
|
||||
bool isReturnTypeIndirect(const CXXRecordDecl *RD) const {
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
return !RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor();
|
||||
}
|
||||
|
||||
RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const {
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (!RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor())
|
||||
return RAA_Indirect;
|
||||
return RAA_Default;
|
||||
}
|
||||
|
||||
bool isZeroInitializable(const MemberPointerType *MPT);
|
||||
|
||||
llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT);
|
||||
|
|
|
@ -28,6 +28,17 @@ class MicrosoftCXXABI : public CGCXXABI {
|
|||
public:
|
||||
MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {}
|
||||
|
||||
bool isReturnTypeIndirect(const CXXRecordDecl *RD) const {
|
||||
// Structures that are not C++03 PODs are always indirect.
|
||||
return !RD->isPOD();
|
||||
}
|
||||
|
||||
RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const {
|
||||
if (RD->hasNonTrivialCopyConstructor())
|
||||
return RAA_DirectInMemory;
|
||||
return RAA_Default;
|
||||
}
|
||||
|
||||
StringRef GetPureVirtualCallName() { return "_purecall"; }
|
||||
// No known support for deleted functions in MSVC yet, so this choice is
|
||||
// arbitrary.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "TargetInfo.h"
|
||||
#include "ABIInfo.h"
|
||||
#include "CGCXXABI.h"
|
||||
#include "CodeGenFunction.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/Frontend/CodeGenOptions.h"
|
||||
|
@ -43,6 +44,37 @@ static bool isAggregateTypeForABI(QualType T) {
|
|||
|
||||
ABIInfo::~ABIInfo() {}
|
||||
|
||||
static bool isRecordReturnIndirect(const RecordType *RT, CodeGen::CodeGenTypes &CGT) {
|
||||
const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
|
||||
if (!RD)
|
||||
return false;
|
||||
return CGT.CGM.getCXXABI().isReturnTypeIndirect(RD);
|
||||
}
|
||||
|
||||
|
||||
static bool isRecordReturnIndirect(QualType T, CodeGen::CodeGenTypes &CGT) {
|
||||
const RecordType *RT = T->getAs<RecordType>();
|
||||
if (!RT)
|
||||
return false;
|
||||
return isRecordReturnIndirect(RT, CGT);
|
||||
}
|
||||
|
||||
static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT,
|
||||
CodeGen::CodeGenTypes &CGT) {
|
||||
const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
|
||||
if (!RD)
|
||||
return CGCXXABI::RAA_Default;
|
||||
return CGT.CGM.getCXXABI().getRecordArgABI(RD);
|
||||
}
|
||||
|
||||
static CGCXXABI::RecordArgABI getRecordArgABI(QualType T,
|
||||
CodeGen::CodeGenTypes &CGT) {
|
||||
const RecordType *RT = T->getAs<RecordType>();
|
||||
if (!RT)
|
||||
return CGCXXABI::RAA_Default;
|
||||
return getRecordArgABI(RT, CGT);
|
||||
}
|
||||
|
||||
ASTContext &ABIInfo::getContext() const {
|
||||
return CGT.getContext();
|
||||
}
|
||||
|
@ -170,27 +202,6 @@ static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/// hasNonTrivialDestructorOrCopyConstructor - Determine if a type has either
|
||||
/// a non-trivial destructor or a non-trivial copy constructor.
|
||||
static bool hasNonTrivialDestructorOrCopyConstructor(const RecordType *RT) {
|
||||
const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
|
||||
if (!RD)
|
||||
return false;
|
||||
|
||||
return !RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor();
|
||||
}
|
||||
|
||||
/// isRecordWithNonTrivialDestructorOrCopyConstructor - Determine if a type is
|
||||
/// a record type with either a non-trivial destructor or a non-trivial copy
|
||||
/// constructor.
|
||||
static bool isRecordWithNonTrivialDestructorOrCopyConstructor(QualType T) {
|
||||
const RecordType *RT = T->getAs<RecordType>();
|
||||
if (!RT)
|
||||
return false;
|
||||
|
||||
return hasNonTrivialDestructorOrCopyConstructor(RT);
|
||||
}
|
||||
|
||||
/// isSingleElementStruct - Determine if a structure is a "single
|
||||
/// element struct", i.e. it has exactly one non-empty field or
|
||||
/// exactly one field which is itself a single element
|
||||
|
@ -370,7 +381,7 @@ ABIArgInfo DefaultABIInfo::classifyArgumentType(QualType Ty) const {
|
|||
if (isAggregateTypeForABI(Ty)) {
|
||||
// Records with non trivial destructors/constructors should not be passed
|
||||
// by value.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
if (isRecordReturnIndirect(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
|
||||
return ABIArgInfo::getIndirect(0);
|
||||
|
@ -440,11 +451,8 @@ llvm::Value *PNaClABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
|||
/// \brief Classify argument of given type \p Ty.
|
||||
ABIArgInfo PNaClABIInfo::classifyArgumentType(QualType Ty) const {
|
||||
if (isAggregateTypeForABI(Ty)) {
|
||||
// In the PNaCl ABI we always pass records/structures on the stack. The
|
||||
// byval attribute can be used if the record doesn't have non-trivial
|
||||
// constructors/destructors.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
return ABIArgInfo::getIndirect(0);
|
||||
} else if (const EnumType *EnumTy = Ty->getAs<EnumType>()) {
|
||||
// Treat an enum type as its underlying type.
|
||||
|
@ -505,7 +513,7 @@ class X86_32ABIInfo : public ABIInfo {
|
|||
|
||||
bool IsDarwinVectorABI;
|
||||
bool IsSmallStructInRegABI;
|
||||
bool IsWin32FloatStructABI;
|
||||
bool IsWin32StructABI;
|
||||
unsigned DefaultNumRegisterParameters;
|
||||
|
||||
static bool isRegisterSize(unsigned Size) {
|
||||
|
@ -540,7 +548,7 @@ public:
|
|||
X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool w,
|
||||
unsigned r)
|
||||
: ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p),
|
||||
IsWin32FloatStructABI(w), DefaultNumRegisterParameters(r) {}
|
||||
IsWin32StructABI(w), DefaultNumRegisterParameters(r) {}
|
||||
};
|
||||
|
||||
class X86_32TargetCodeGenInfo : public TargetCodeGenInfo {
|
||||
|
@ -666,9 +674,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy,
|
|||
|
||||
if (isAggregateTypeForABI(RetTy)) {
|
||||
if (const RecordType *RT = RetTy->getAs<RecordType>()) {
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (hasNonTrivialDestructorOrCopyConstructor(RT))
|
||||
if (isRecordReturnIndirect(RT, CGT))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
|
||||
// Structures with flexible arrays are always indirect.
|
||||
|
@ -692,7 +698,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy,
|
|||
// We apply a similar transformation for pointer types to improve the
|
||||
// quality of the generated IR.
|
||||
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
|
||||
if ((!IsWin32FloatStructABI && SeltTy->isRealFloatingType())
|
||||
if ((!IsWin32StructABI && SeltTy->isRealFloatingType())
|
||||
|| SeltTy->hasPointerRepresentation())
|
||||
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
|
||||
|
||||
|
@ -849,13 +855,14 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty,
|
|||
bool IsFastCall) const {
|
||||
// FIXME: Set alignment on indirect arguments.
|
||||
if (isAggregateTypeForABI(Ty)) {
|
||||
// Structures with flexible arrays are always indirect.
|
||||
if (const RecordType *RT = Ty->getAs<RecordType>()) {
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (hasNonTrivialDestructorOrCopyConstructor(RT))
|
||||
return getIndirectResult(Ty, false, FreeRegs);
|
||||
if (IsWin32StructABI)
|
||||
return getIndirectResult(Ty, true, FreeRegs);
|
||||
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(RT, CGT))
|
||||
return getIndirectResult(Ty, RAA == CGCXXABI::RAA_DirectInMemory, FreeRegs);
|
||||
|
||||
// Structures with flexible arrays are always indirect.
|
||||
if (RT->getDecl()->hasFlexibleArrayMember())
|
||||
return getIndirectResult(Ty, true, FreeRegs);
|
||||
}
|
||||
|
@ -1182,7 +1189,7 @@ public:
|
|||
/// WinX86_64ABIInfo - The Windows X86_64 ABI information.
|
||||
class WinX86_64ABIInfo : public ABIInfo {
|
||||
|
||||
ABIArgInfo classify(QualType Ty) const;
|
||||
ABIArgInfo classify(QualType Ty, bool IsReturnType) const;
|
||||
|
||||
public:
|
||||
WinX86_64ABIInfo(CodeGen::CodeGenTypes &CGT) : ABIInfo(CGT) {}
|
||||
|
@ -1528,7 +1535,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase,
|
|||
// AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial
|
||||
// copy constructor or a non-trivial destructor, it is passed by invisible
|
||||
// reference.
|
||||
if (hasNonTrivialDestructorOrCopyConstructor(RT))
|
||||
if (getRecordArgABI(RT, CGT))
|
||||
return;
|
||||
|
||||
const RecordDecl *RD = RT->getDecl();
|
||||
|
@ -1678,8 +1685,8 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty,
|
|||
ABIArgInfo::getExtend() : ABIArgInfo::getDirect());
|
||||
}
|
||||
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
|
||||
// Compute the byval alignment. We specify the alignment of the byval in all
|
||||
// cases so that the mid-level optimizer knows the alignment of the byval.
|
||||
|
@ -2167,7 +2174,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(
|
|||
// COMPLEX_X87, it is passed in memory.
|
||||
case X87:
|
||||
case ComplexX87:
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
if (getRecordArgABI(Ty, CGT) == CGCXXABI::RAA_Indirect)
|
||||
++neededInt;
|
||||
return getIndirectResult(Ty, freeIntRegs);
|
||||
|
||||
|
@ -2498,7 +2505,7 @@ llvm::Value *X86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
|||
return ResAddr;
|
||||
}
|
||||
|
||||
ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const {
|
||||
ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, bool IsReturnType) const {
|
||||
|
||||
if (Ty->isVoidType())
|
||||
return ABIArgInfo::getIgnore();
|
||||
|
@ -2509,8 +2516,15 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const {
|
|||
uint64_t Size = getContext().getTypeSize(Ty);
|
||||
|
||||
if (const RecordType *RT = Ty->getAs<RecordType>()) {
|
||||
if (hasNonTrivialDestructorOrCopyConstructor(RT) ||
|
||||
RT->getDecl()->hasFlexibleArrayMember())
|
||||
if (IsReturnType) {
|
||||
if (isRecordReturnIndirect(RT, CGT))
|
||||
return ABIArgInfo::getIndirect(0, false);
|
||||
} else {
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(RT, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
}
|
||||
|
||||
if (RT->getDecl()->hasFlexibleArrayMember())
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
|
||||
// FIXME: mingw-w64-gcc emits 128-bit struct as i128
|
||||
|
@ -2537,11 +2551,11 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const {
|
|||
void WinX86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
||||
|
||||
QualType RetTy = FI.getReturnType();
|
||||
FI.getReturnInfo() = classify(RetTy);
|
||||
FI.getReturnInfo() = classify(RetTy, true);
|
||||
|
||||
for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end();
|
||||
it != ie; ++it)
|
||||
it->info = classify(it->type);
|
||||
it->info = classify(it->type, false);
|
||||
}
|
||||
|
||||
llvm::Value *WinX86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||
|
@ -2768,10 +2782,8 @@ PPC64_SVR4_ABIInfo::classifyArgumentType(QualType Ty) const {
|
|||
return ABIArgInfo::getDirect();
|
||||
|
||||
if (isAggregateTypeForABI(Ty)) {
|
||||
// Records with non trivial destructors/constructors should not be passed
|
||||
// by value.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
|
||||
return ABIArgInfo::getIndirect(0);
|
||||
}
|
||||
|
@ -3235,10 +3247,8 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, int *VFPRegs,
|
|||
if (isEmptyRecord(getContext(), Ty, true))
|
||||
return ABIArgInfo::getIgnore();
|
||||
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
|
||||
if (getABIKind() == ARMABIInfo::AAPCS_VFP) {
|
||||
// Homogeneous Aggregates need to be expanded when we can fit the aggregate
|
||||
|
@ -3401,7 +3411,7 @@ ABIArgInfo ARMABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy))
|
||||
if (isRecordReturnIndirect(RetTy, CGT))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
|
||||
// Are we following APCS?
|
||||
|
@ -3725,12 +3735,10 @@ ABIArgInfo AArch64ABIInfo::classifyGenericType(QualType Ty,
|
|||
return tryUseRegs(Ty, FreeIntRegs, RegsNeeded, /*IsInt=*/ true);
|
||||
}
|
||||
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) {
|
||||
if (FreeIntRegs > 0)
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) {
|
||||
if (FreeIntRegs > 0 && RAA == CGCXXABI::RAA_Indirect)
|
||||
--FreeIntRegs;
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
}
|
||||
|
||||
if (isEmptyRecord(getContext(), Ty, true)) {
|
||||
|
@ -4415,11 +4423,9 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const {
|
|||
if (TySize == 0)
|
||||
return ABIArgInfo::getIgnore();
|
||||
|
||||
// Records with non trivial destructors/constructors should not be passed
|
||||
// by value.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) {
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) {
|
||||
Offset = OrigOffset + MinABIStackAlignInBytes;
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
}
|
||||
|
||||
// If we have reached here, aggregates are passed directly by coercing to
|
||||
|
@ -4489,6 +4495,9 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
return ABIArgInfo::getIgnore();
|
||||
|
||||
if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) {
|
||||
if (isRecordReturnIndirect(RetTy, CGT))
|
||||
return ABIArgInfo::getIndirect(0);
|
||||
|
||||
if (Size <= 128) {
|
||||
if (RetTy->isAnyComplexType())
|
||||
return ABIArgInfo::getDirect();
|
||||
|
@ -4497,7 +4506,7 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
if (IsO32 && RetTy->isVectorType() && !RetTy->hasFloatingRepresentation())
|
||||
return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size));
|
||||
|
||||
if (!IsO32 && !isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy))
|
||||
if (!IsO32)
|
||||
return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size));
|
||||
}
|
||||
|
||||
|
@ -4707,10 +4716,8 @@ ABIArgInfo HexagonABIInfo::classifyArgumentType(QualType Ty) const {
|
|||
if (isEmptyRecord(getContext(), Ty, true))
|
||||
return ABIArgInfo::getIgnore();
|
||||
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT))
|
||||
return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory);
|
||||
|
||||
uint64_t Size = getContext().getTypeSize(Ty);
|
||||
if (Size > 64)
|
||||
|
@ -4745,7 +4752,7 @@ ABIArgInfo HexagonABIInfo::classifyReturnType(QualType RetTy) const {
|
|||
|
||||
// Structures with either a non-trivial destructor or a non-trivial
|
||||
// copy constructor are always indirect.
|
||||
if (isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy))
|
||||
if (isRecordReturnIndirect(RetTy, CGT))
|
||||
return ABIArgInfo::getIndirect(0, /*ByVal=*/false);
|
||||
|
||||
if (isEmptyRecord(getContext(), RetTy, true))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// RUN: %clang_cc1 -w -triple i386-pc-win32 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
// CHECK: define i64 @f1_1()
|
||||
// CHECK: define void @f1_2(i32 %a0.0, i32 %a0.1)
|
||||
// CHECK: define void @f1_2(%struct.s1* byval align 4 %a0)
|
||||
struct s1 {
|
||||
int a;
|
||||
int b;
|
||||
|
@ -31,7 +31,7 @@ struct s4 {
|
|||
struct s4 f4_1(void) { while (1) {} }
|
||||
|
||||
// CHECK: define i64 @f5_1()
|
||||
// CHECK: define void @f5_2(double %a0.0)
|
||||
// CHECK: define void @f5_2(%struct.s5* byval align 4)
|
||||
struct s5 {
|
||||
double a;
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ struct s5 f5_1(void) { while (1) {} }
|
|||
void f5_2(struct s5 a0) {}
|
||||
|
||||
// CHECK: define i32 @f6_1()
|
||||
// CHECK: define void @f6_2(float %a0.0)
|
||||
// CHECK: define void @f6_2(%struct.s6* byval align 4 %a0)
|
||||
struct s6 {
|
||||
float a;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-linux | FileCheck -check-prefix LINUX %s
|
||||
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-win32 -cxx-abi microsoft | FileCheck -check-prefix WIN32 %s
|
||||
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-pc-win32 -cxx-abi microsoft | FileCheck -check-prefix WIN64 %s
|
||||
|
||||
struct Empty {};
|
||||
|
||||
struct EmptyWithCtor {
|
||||
EmptyWithCtor() {}
|
||||
};
|
||||
|
||||
struct Small {
|
||||
int x;
|
||||
};
|
||||
|
||||
// This is a C++11 trivial and standard-layout struct but not a C++03 POD.
|
||||
struct SmallCpp11NotCpp03Pod : Empty {
|
||||
int x;
|
||||
};
|
||||
|
||||
struct SmallWithCtor {
|
||||
SmallWithCtor() {}
|
||||
int x;
|
||||
};
|
||||
|
||||
struct SmallWithVftable {
|
||||
int x;
|
||||
virtual void foo();
|
||||
};
|
||||
|
||||
struct Medium {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct MediumWithCopyCtor {
|
||||
MediumWithCopyCtor();
|
||||
MediumWithCopyCtor(const struct MediumWithCopyCtor &);
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct Big {
|
||||
int a, b, c, d, e, f;
|
||||
};
|
||||
|
||||
// Returning structs that fit into a register.
|
||||
Small small_return() { return Small(); }
|
||||
// LINUX: define void @_Z12small_returnv(%struct.Small* noalias sret %agg.result)
|
||||
// WIN32: define i32 @"\01?small_return@@YA?AUSmall@@XZ"()
|
||||
// WIN64: define i32 @"\01?small_return@@YA?AUSmall@@XZ"()
|
||||
|
||||
Medium medium_return() { return Medium(); }
|
||||
// LINUX: define void @_Z13medium_returnv(%struct.Medium* noalias sret %agg.result)
|
||||
// WIN32: define i64 @"\01?medium_return@@YA?AUMedium@@XZ"()
|
||||
// WIN64: define i64 @"\01?medium_return@@YA?AUMedium@@XZ"()
|
||||
|
||||
// Returning structs that fit into a register but are not POD.
|
||||
SmallCpp11NotCpp03Pod small_non_pod_return() { return SmallCpp11NotCpp03Pod(); }
|
||||
// LINUX: define void @_Z20small_non_pod_returnv(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result)
|
||||
// WIN32: define void @"\01?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result)
|
||||
// WIN64: define void @"\01?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result)
|
||||
|
||||
SmallWithCtor small_with_ctor_return() { return SmallWithCtor(); }
|
||||
// LINUX: define void @_Z22small_with_ctor_returnv(%struct.SmallWithCtor* noalias sret %agg.result)
|
||||
// WIN32: define void @"\01?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result)
|
||||
// WIN64: define void @"\01?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result)
|
||||
|
||||
SmallWithVftable small_with_vftable_return() { return SmallWithVftable(); }
|
||||
// LINUX: define void @_Z25small_with_vftable_returnv(%struct.SmallWithVftable* noalias sret %agg.result)
|
||||
// WIN32: define void @"\01?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result)
|
||||
// WIN64: define void @"\01?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result)
|
||||
|
||||
MediumWithCopyCtor medium_with_copy_ctor_return() { return MediumWithCopyCtor(); }
|
||||
// LINUX: define void @_Z28medium_with_copy_ctor_returnv(%struct.MediumWithCopyCtor* noalias sret %agg.result)
|
||||
// WIN32: define void @"\01?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result)
|
||||
// WIN64: define void @"\01?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result)
|
||||
|
||||
// Returning a large struct that doesn't fit into a register.
|
||||
Big big_return() { return Big(); }
|
||||
// LINUX: define void @_Z10big_returnv(%struct.Big* noalias sret %agg.result)
|
||||
// WIN32: define void @"\01?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result)
|
||||
// WIN64: define void @"\01?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result)
|
||||
|
||||
|
||||
void small_arg(Small s) {}
|
||||
// LINUX: define void @_Z9small_arg5Small(%struct.Small* byval align 4 %s)
|
||||
// WIN32: define void @"\01?small_arg@@YAXUSmall@@@Z"(%struct.Small* byval align 4 %s)
|
||||
// WIN64: define void @"\01?small_arg@@YAXUSmall@@@Z"(i32 %s.coerce)
|
||||
|
||||
void medium_arg(Medium s) {}
|
||||
// LINUX: define void @_Z10medium_arg6Medium(%struct.Medium* byval align 4 %s)
|
||||
// WIN32: define void @"\01?medium_arg@@YAXUMedium@@@Z"(%struct.Medium* byval align 4 %s)
|
||||
// WIN64: define void @"\01?medium_arg@@YAXUMedium@@@Z"(i64 %s.coerce)
|
||||
|
||||
void small_arg_with_ctor(SmallWithCtor s) {}
|
||||
// LINUX: define void @_Z19small_arg_with_ctor13SmallWithCtor(%struct.SmallWithCtor* byval align 4 %s)
|
||||
// WIN32: define void @"\01?small_arg_with_ctor@@YAXUSmallWithCtor@@@Z"(%struct.SmallWithCtor* byval align 4 %s)
|
||||
// WIN64: define void @"\01?small_arg_with_ctor@@YAXUSmallWithCtor@@@Z"(i32 %s.coerce)
|
||||
|
||||
void small_arg_with_vftable(SmallWithVftable s) {}
|
||||
// LINUX: define void @_Z22small_arg_with_vftable16SmallWithVftable(%struct.SmallWithVftable* %s)
|
||||
// WIN32: define void @"\01?small_arg_with_vftable@@YAXUSmallWithVftable@@@Z"(%struct.SmallWithVftable* byval align 4 %s)
|
||||
// WIN64: define void @"\01?small_arg_with_vftable@@YAXUSmallWithVftable@@@Z"(%struct.SmallWithVftable* byval %s)
|
||||
|
||||
void medium_arg_with_copy_ctor(MediumWithCopyCtor s) {}
|
||||
// LINUX: define void @_Z25medium_arg_with_copy_ctor18MediumWithCopyCtor(%struct.MediumWithCopyCtor* %s)
|
||||
// WIN32: define void @"\01?medium_arg_with_copy_ctor@@YAXUMediumWithCopyCtor@@@Z"(%struct.MediumWithCopyCtor* byval align 4 %s)
|
||||
// WIN64: define void @"\01?medium_arg_with_copy_ctor@@YAXUMediumWithCopyCtor@@@Z"(%struct.MediumWithCopyCtor* byval %s)
|
||||
|
||||
void big_arg(Big s) {}
|
||||
// LINUX: define void @_Z7big_arg3Big(%struct.Big* byval align 4 %s)
|
||||
// WIN32: define void @"\01?big_arg@@YAXUBig@@@Z"(%struct.Big* byval align 4 %s)
|
||||
// WIN64: define void @"\01?big_arg@@YAXUBig@@@Z"(%struct.Big* %s)
|
||||
|
||||
// FIXME: Add WIN64 tests. Currently, even the method manglings are wrong (sic!).
|
||||
class Class {
|
||||
public:
|
||||
Small thiscall_method_small() { return Small(); }
|
||||
// LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this)
|
||||
// WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this)
|
||||
|
||||
SmallWithCtor thiscall_method_small_with_ctor() { return SmallWithCtor(); }
|
||||
// LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this)
|
||||
// WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this)
|
||||
|
||||
Small __cdecl cdecl_method_small() { return Small(); }
|
||||
// LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this)
|
||||
// FIXME: Interesting, cdecl returns structures differently for instance
|
||||
// methods and global functions. This is not supported by Clang yet...
|
||||
// FIXME: Replace WIN32-NOT with WIN32 when this is fixed.
|
||||
// WIN32-NOT: define {{.*}} void @"\01?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this)
|
||||
|
||||
Big __cdecl cdecl_method_big() { return Big(); }
|
||||
// LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret %agg.result, %class.Class* %this)
|
||||
// WIN32: define {{.*}} void @"\01?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result, %class.Class* %this)
|
||||
|
||||
void thiscall_method_arg(Empty s) {}
|
||||
// LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Empty(%class.Class* %this)
|
||||
// WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUEmpty@@@Z"(%class.Class* %this, %struct.Empty* byval align 4 %s)
|
||||
|
||||
void thiscall_method_arg(EmptyWithCtor s) {}
|
||||
// LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE13EmptyWithCtor(%class.Class* %this)
|
||||
// WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUEmptyWithCtor@@@Z"(%class.Class* %this, %struct.EmptyWithCtor* byval align 4 %s)
|
||||
|
||||
void thiscall_method_arg(Small s) {}
|
||||
// LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Small(%class.Class* %this, %struct.Small* byval align 4 %s)
|
||||
// WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUSmall@@@Z"(%class.Class* %this, %struct.Small* byval align 4 %s)
|
||||
|
||||
void thiscall_method_arg(SmallWithCtor s) {}
|
||||
// LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE13SmallWithCtor(%class.Class* %this, %struct.SmallWithCtor* byval align 4 %s)
|
||||
// WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUSmallWithCtor@@@Z"(%class.Class* %this, %struct.SmallWithCtor* byval align 4 %s)
|
||||
|
||||
void thiscall_method_arg(Big s) {}
|
||||
// LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE3Big(%class.Class* %this, %struct.Big* byval align 4 %s)
|
||||
// WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUBig@@@Z"(%class.Class* %this, %struct.Big* byval align 4 %s)
|
||||
};
|
||||
|
||||
void use_class() {
|
||||
Class c;
|
||||
c.thiscall_method_small();
|
||||
c.thiscall_method_small_with_ctor();
|
||||
|
||||
c.cdecl_method_small();
|
||||
c.cdecl_method_big();
|
||||
|
||||
c.thiscall_method_arg(Empty());
|
||||
c.thiscall_method_arg(EmptyWithCtor());
|
||||
c.thiscall_method_arg(Small());
|
||||
c.thiscall_method_arg(SmallWithCtor());
|
||||
c.thiscall_method_arg(Big());
|
||||
}
|
Loading…
Reference in New Issue