summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/Decl.h13
-rw-r--r--clang/include/clang/AST/DeclCXX.h7
-rw-r--r--clang/include/clang/AST/Type.h5
-rw-r--r--clang/include/clang/Basic/LangOptions.def4
-rw-r--r--clang/include/clang/Basic/LangOptions.h17
-rw-r--r--clang/include/clang/Basic/TargetInfo.h8
-rw-r--r--clang/include/clang/Frontend/CodeGenOptions.def3
-rw-r--r--clang/lib/AST/ASTContext.cpp8
-rw-r--r--clang/lib/AST/Decl.cpp2
-rw-r--r--clang/lib/AST/DeclCXX.cpp12
-rw-r--r--clang/lib/AST/Type.cpp6
-rw-r--r--clang/lib/Basic/TargetInfo.cpp8
-rw-r--r--clang/lib/Basic/Targets/X86.h5
-rw-r--r--clang/lib/CodeGen/CGCall.cpp15
-rw-r--r--clang/lib/CodeGen/ItaniumCXXABI.cpp7
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp55
-rw-r--r--clang/lib/CodeGen/TargetInfo.cpp4
-rw-r--r--clang/lib/Frontend/CompilerInvocation.cpp54
-rw-r--r--clang/lib/Sema/SemaDecl.cpp4
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp98
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp2
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp1
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp3
-rw-r--r--clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp3
-rw-r--r--clang/test/CodeGenObjCXX/arc-special-member-functions.mm9
-rw-r--r--clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm (renamed from clang/test/CodeGenObjCXX/trivial_abi.mm)56
-rw-r--r--clang/test/CodeGenObjCXX/property-dot-copy-elision.mm2
27 files changed, 269 insertions, 142 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 41b47eacee1..44d2030b4f6 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3559,6 +3559,11 @@ class RecordDecl : public TagDecl {
/// pass an object of this class.
bool CanPassInRegisters : 1;
+ /// Indicates whether this struct is destroyed in the callee. This flag is
+ /// meaningless when Microsoft ABI is used since parameters are always
+ /// destroyed in the callee.
+ bool ParamDestroyedInCallee : 1;
+
protected:
RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
@@ -3654,6 +3659,14 @@ public:
CanPassInRegisters = CanPass;
}
+ bool isParamDestroyedInCallee() const {
+ return ParamDestroyedInCallee;
+ }
+
+ void setParamDestroyedInCallee(bool V) {
+ ParamDestroyedInCallee = V;
+ }
+
/// \brief Determines whether this declaration represents the
/// injected class name.
///
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index e9f6f9ef6e7..a5e16771c2c 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1468,13 +1468,6 @@ public:
return data().HasIrrelevantDestructor;
}
- /// Determine whether the triviality for the purpose of calls for this class
- /// is overridden to be trivial because this class or the type of one of its
- /// subobjects has attribute "trivial_abi".
- bool hasTrivialABIOverride() const {
- return canPassInRegisters() && hasNonTrivialDestructor();
- }
-
/// \brief Determine whether this class has a non-literal or/ volatile type
/// non-static data member or base class.
bool hasNonLiteralTypeFieldsOrBases() const {
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index bb8944aa6e5..f8a4dcc59a2 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -808,11 +808,6 @@ public:
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
bool isTriviallyCopyableType(const ASTContext &Context) const;
- /// Determine whether this is a class whose triviality for the purpose of
- /// calls is overridden to be trivial because the class or the type of one of
- /// its subobjects has attribute "trivial_abi".
- bool hasTrivialABIOverride() const;
-
// Don't promise in the API that anything besides 'const' can be
// easily added.
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 69f67a9e2ef..0802bbefcf8 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -284,6 +284,10 @@ LANGOPT(XRayAlwaysEmitCustomEvents, 1, 0,
BENIGN_LANGOPT(AllowEditorPlaceholders, 1, 0,
"allow editor placeholders in source")
+ENUM_LANGOPT(ClangABICompat, ClangABI, 4, ClangABI::Latest,
+ "version of Clang that we should attempt to be ABI-compatible "
+ "with")
+
#undef LANGOPT
#undef COMPATIBLE_LANGOPT
#undef BENIGN_LANGOPT
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 42ec9f3e409..048ccc6bec8 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -102,6 +102,23 @@ public:
MSVC2015 = 19
};
+ /// Clang versions with different platform ABI conformance.
+ enum class ClangABI {
+ /// Attempt to be ABI-compatible with code generated by Clang 3.8.x
+ /// (SVN r257626). This causes <1 x long long> to be passed in an
+ /// integer register instead of an SSE register on x64_64.
+ Ver3_8,
+
+ /// Attempt to be ABI-compatible with code generated by Clang 4.0.x
+ /// (SVN r291814). This causes move operations to be ignored when
+ /// determining whether a class type can be passed or returned directly.
+ Ver4,
+
+ /// Conform to the underlying platform's C and C++ ABIs as closely
+ /// as we can.
+ Latest
+ };
+
enum FPContractModeKind {
// Form fused FP ops only where result will not be affected.
FPC_Off,
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 9f4e0044d8d..26f72a1814d 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1053,6 +1053,14 @@ public:
}
}
+ enum CallingConvKind {
+ CCK_Default,
+ CCK_ClangABI4OrPS4,
+ CCK_MicrosoftX86_64
+ };
+
+ virtual CallingConvKind getCallingConvKind(bool ClangABICompat4) const;
+
/// Controls if __builtin_longjmp / __builtin_setjmp can be lowered to
/// llvm.eh.sjlj.longjmp / llvm.eh.sjlj.setjmp.
virtual bool hasSjLjLowering() const {
diff --git a/clang/include/clang/Frontend/CodeGenOptions.def b/clang/include/clang/Frontend/CodeGenOptions.def
index edf266f743c..adcbe36fb41 100644
--- a/clang/include/clang/Frontend/CodeGenOptions.def
+++ b/clang/include/clang/Frontend/CodeGenOptions.def
@@ -138,9 +138,6 @@ ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy)
CODEGENOPT(OmitLeafFramePointer , 1, 0) ///< Set when -momit-leaf-frame-pointer is
///< enabled.
-/// A version of Clang that we should attempt to be ABI-compatible with.
-ENUM_CODEGENOPT(ClangABICompat, ClangABI, 4, ClangABI::Latest)
-
VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified.
VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 77c06f21dfb..ea96a077631 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2643,9 +2643,11 @@ void ASTContext::adjustExceptionSpec(
}
bool ASTContext::isParamDestroyedInCallee(QualType T) const {
- return getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee() ||
- T.hasTrivialABIOverride() ||
- T.isDestructedType() == QualType::DK_nontrivial_c_struct;
+ if (getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee())
+ return true;
+ if (const auto *RT = T->getBaseElementTypeUnsafe()->getAs<RecordType>())
+ return RT->getDecl()->isParamDestroyedInCallee();
+ return false;
}
/// getComplexType - Return the uniqued reference to the type for a complex
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index ed80dd8fca2..4fb687d0fdd 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3951,7 +3951,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
LoadedFieldsFromExternalStorage(false),
NonTrivialToPrimitiveDefaultInitialize(false),
NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false),
- CanPassInRegisters(true) {
+ CanPassInRegisters(true), ParamDestroyedInCallee(false) {
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
}
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 271872352cd..1e1ee439d8d 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -801,7 +801,17 @@ void CXXRecordDecl::addedMember(Decl *D) {
struct DefinitionData &Data = data();
Data.PlainOldData = false;
Data.HasTrivialSpecialMembers = 0;
- Data.HasTrivialSpecialMembersForCall = 0;
+
+ // __strong or __weak fields do not make special functions non-trivial
+ // for the purpose of calls.
+ Qualifiers::ObjCLifetime LT = T.getQualifiers().getObjCLifetime();
+ if (LT != Qualifiers::OCL_Strong && LT != Qualifiers::OCL_Weak)
+ data().HasTrivialSpecialMembersForCall = 0;
+
+ // Structs with __weak fields should never be passed directly.
+ if (LT == Qualifiers::OCL_Weak)
+ setCanPassInRegisters(false);
+
Data.HasIrrelevantDestructor = false;
} else if (!Context.getLangOpts().ObjCAutoRefCount) {
setHasObjectMember(true);
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 2985aac66b2..d8b9967cdc8 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2195,12 +2195,6 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
return false;
}
-bool QualType::hasTrivialABIOverride() const {
- if (const auto *RD = getTypePtr()->getAsCXXRecordDecl())
- return RD->hasTrivialABIOverride();
- return false;
-}
-
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 36c8b106c7b..497ae34895e 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -357,6 +357,14 @@ bool TargetInfo::initFeatureMap(
return true;
}
+TargetInfo::CallingConvKind
+TargetInfo::getCallingConvKind(bool ClangABICompat4) const {
+ if (getCXXABI() != TargetCXXABI::Microsoft &&
+ (ClangABICompat4 || getTriple().getOS() == llvm::Triple::PS4))
+ return CCK_ClangABI4OrPS4;
+ return CCK_Default;
+}
+
LangAS TargetInfo::getOpenCLTypeAddrSpace(OpenCLTypeKind TK) const {
switch (TK) {
case OCLTK_Image:
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 660e5847d68..f8029400e80 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -728,6 +728,11 @@ public:
Builder.defineMacro("_M_X64", "100");
Builder.defineMacro("_M_AMD64", "100");
}
+
+ TargetInfo::CallingConvKind
+ getCallingConvKind(bool ClangABICompat4) const override {
+ return CCK_MicrosoftX86_64;
+ }
};
// x86-64 MinGW target
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 784797eea64..01caadba92d 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3540,24 +3540,13 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
else
Slot = CreateAggTemp(type, "agg.tmp");
- bool DestroyedInCallee = true, NeedsEHCleanup = true;
- if (const auto *RD = type->getAsCXXRecordDecl()) {
- DestroyedInCallee =
- RD && RD->hasNonTrivialDestructor() &&
- (CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default ||
- RD->hasTrivialABIOverride());
- } else {
- NeedsEHCleanup = needsEHCleanup(type.isDestructedType());
- }
-
- if (DestroyedInCallee)
- Slot.setExternallyDestructed();
+ Slot.setExternallyDestructed();
EmitAggExpr(E, Slot);
RValue RV = Slot.asRValue();
args.add(RV, type);
- if (DestroyedInCallee && NeedsEHCleanup) {
+ if (type->getAsCXXRecordDecl() || needsEHCleanup(type.isDestructedType())) {
// Create a no-op GEP between the placeholder and the cleanup so we can
// RAUW it successfully. It also serves as a marker of the first
// instruction where the cleanup is active.
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 20021808f75..55e74cd4258 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -63,13 +63,6 @@ public:
bool classifyReturnType(CGFunctionInfo &FI) const override;
bool passClassIndirect(const CXXRecordDecl *RD) const {
- // Clang <= 4 used the pre-C++11 rule, which ignores move operations.
- // The PS4 platform ABI follows the behavior of Clang 3.2.
- if (CGM.getCodeGenOpts().getClangABICompat() <=
- CodeGenOptions::ClangABI::Ver4 ||
- CGM.getTriple().getOS() == llvm::Triple::PS4)
- return RD->hasNonTrivialDestructorForCall() ||
- RD->hasNonTrivialCopyConstructorForCall();
return !canCopyArgument(RD);
}
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 6ee423bb82e..542e1bf5fa3 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -829,60 +829,7 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
return RAA_Default;
case llvm::Triple::x86_64:
- bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false;
- bool DtorIsTrivialForCall = false;
-
- // If a class has at least one non-deleted, trivial copy constructor, it
- // is passed according to the C ABI. Otherwise, it is passed indirectly.
- //
- // Note: This permits classes with non-trivial copy or move ctors to be
- // passed in registers, so long as they *also* have a trivial copy ctor,
- // which is non-conforming.
- if (RD->needsImplicitCopyConstructor()) {
- if (!RD->defaultedCopyConstructorIsDeleted()) {
- if (RD->hasTrivialCopyConstructor())
- CopyCtorIsTrivial = true;
- if (RD->hasTrivialCopyConstructorForCall())
- CopyCtorIsTrivialForCall = true;
- }
- } else {
- for (const CXXConstructorDecl *CD : RD->ctors()) {
- if (CD->isCopyConstructor() && !CD->isDeleted()) {
- if (CD->isTrivial())
- CopyCtorIsTrivial = true;
- if (CD->isTrivialForCall())
- CopyCtorIsTrivialForCall = true;
- }
- }
- }
-
- if (RD->needsImplicitDestructor()) {
- if (!RD->defaultedDestructorIsDeleted() &&
- RD->hasTrivialDestructorForCall())
- DtorIsTrivialForCall = true;
- } else if (const auto *D = RD->getDestructor()) {
- if (!D->isDeleted() && D->isTrivialForCall())
- DtorIsTrivialForCall = true;
- }
-
- // If the copy ctor and dtor are both trivial-for-calls, pass direct.
- if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall)
- return RAA_Default;
-
- // If a class has a destructor, we'd really like to pass it indirectly
- // because it allows us to elide copies. Unfortunately, MSVC makes that
- // impossible for small types, which it will pass in a single register or
- // stack slot. Most objects with dtors are large-ish, so handle that early.
- // We can't call out all large objects as being indirect because there are
- // multiple x64 calling conventions and the C++ ABI code shouldn't dictate
- // how we pass large POD types.
-
- // Note: This permits small classes with nontrivial destructors to be
- // passed in registers, which is non-conforming.
- if (CopyCtorIsTrivial &&
- getContext().getTypeSize(RD->getTypeForDecl()) <= 64)
- return RAA_Default;
- return RAA_Indirect;
+ return !canCopyArgument(RD) ? RAA_Indirect : RAA_Default;
}
llvm_unreachable("invalid enum");
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 45c0ec41e0e..8f3676e7d2e 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -2131,8 +2131,8 @@ class X86_64ABIInfo : public SwiftABIInfo {
/// classify it as INTEGER (for compatibility with older clang compilers).
bool classifyIntegerMMXAsSSE() const {
// Clang <= 3.8 did not do this.
- if (getCodeGenOpts().getClangABICompat() <=
- CodeGenOptions::ClangABI::Ver3_8)
+ if (getContext().getLangOpts().getClangABICompat() <=
+ LangOptions::ClangABI::Ver3_8)
return false;
const llvm::Triple &Triple = getTarget().getTriple();
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index d8bd500cde0..84eee0e75cd 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -633,33 +633,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
if (!Opts.ProfileInstrumentUsePath.empty())
setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath);
- if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Latest);
-
- StringRef Ver = A->getValue();
- std::pair<StringRef, StringRef> VerParts = Ver.split('.');
- unsigned Major, Minor = 0;
-
- // Check the version number is valid: either 3.x (0 <= x <= 9) or
- // y or y.0 (4 <= y <= current version).
- if (!VerParts.first.startswith("0") &&
- !VerParts.first.getAsInteger(10, Major) &&
- 3 <= Major && Major <= CLANG_VERSION_MAJOR &&
- (Major == 3 ? VerParts.second.size() == 1 &&
- !VerParts.second.getAsInteger(10, Minor)
- : VerParts.first.size() == Ver.size() ||
- VerParts.second == "0")) {
- // Got a valid version number.
- if (Major == 3 && Minor <= 8)
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver3_8);
- else if (Major <= 4)
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver4);
- } else if (Ver != "latest") {
- Diags.Report(diag::err_drv_invalid_value)
- << A->getAsString(Args) << A->getValue();
- }
- }
-
Opts.CoverageMapping =
Args.hasFlag(OPT_fcoverage_mapping, OPT_fno_coverage_mapping, false);
Opts.DumpCoverageMapping = Args.hasArg(OPT_dump_coverage_mapping);
@@ -2670,6 +2643,33 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
// -fallow-editor-placeholders
Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders);
+
+ if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
+ Opts.setClangABICompat(LangOptions::ClangABI::Latest);
+
+ StringRef Ver = A->getValue();
+ std::pair<StringRef, StringRef> VerParts = Ver.split('.');
+ unsigned Major, Minor = 0;
+
+ // Check the version number is valid: either 3.x (0 <= x <= 9) or
+ // y or y.0 (4 <= y <= current version).
+ if (!VerParts.first.startswith("0") &&
+ !VerParts.first.getAsInteger(10, Major) &&
+ 3 <= Major && Major <= CLANG_VERSION_MAJOR &&
+ (Major == 3 ? VerParts.second.size() == 1 &&
+ !VerParts.second.getAsInteger(10, Minor)
+ : VerParts.first.size() == Ver.size() ||
+ VerParts.second == "0")) {
+ // Got a valid version number.
+ if (Major == 3 && Minor <= 8)
+ Opts.setClangABICompat(LangOptions::ClangABI::Ver3_8);
+ else if (Major <= 4)
+ Opts.setClangABICompat(LangOptions::ClangABI::Ver4);
+ } else if (Ver != "latest") {
+ Diags.Report(diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ }
+ }
}
static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6cfc5012f67..a4ae32e460c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15461,8 +15461,10 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
QualType::PrimitiveCopyKind PCK = FT.isNonTrivialToPrimitiveCopy();
if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial)
Record->setNonTrivialToPrimitiveCopy(true);
- if (FT.isDestructedType())
+ if (FT.isDestructedType()) {
Record->setNonTrivialToPrimitiveDestroy(true);
+ Record->setParamDestroyedInCallee(true);
+ }
if (!FT.canPassInRegisters())
Record->setCanPassInRegisters(false);
}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a56869f6234..f8a3d9444aa 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -5791,12 +5791,21 @@ static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD,
}
}
-/// Determine whether a type is permitted to be passed or returned in
-/// registers, per C++ [class.temporary]p3.
-static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
+/// Determine whether a type would be destructed in the callee if it had a
+/// non-trivial destructor. The rules here are based on C++ [class.temporary]p3,
+/// which determines whether a struct can be passed to or returned from
+/// functions in registers.
+static bool paramCanBeDestroyedInCallee(Sema &S, CXXRecordDecl *D,
+ TargetInfo::CallingConvKind CCK) {
if (D->isDependentType() || D->isInvalidDecl())
return false;
+ // Clang <= 4 used the pre-C++11 rule, which ignores move operations.
+ // The PS4 platform ABI follows the behavior of Clang 3.2.
+ if (CCK == TargetInfo::CCK_ClangABI4OrPS4)
+ return !D->hasNonTrivialDestructorForCall() &&
+ !D->hasNonTrivialCopyConstructorForCall();
+
// Per C++ [class.temporary]p3, the relevant condition is:
// each copy constructor, move constructor, and destructor of X is
// either trivial or deleted, and X has at least one non-deleted copy
@@ -5838,6 +5847,77 @@ static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
return HasNonDeletedCopyOrMove;
}
+static bool computeCanPassInRegister(bool DestroyedInCallee,
+ const CXXRecordDecl *RD,
+ TargetInfo::CallingConvKind CCK,
+ Sema &S) {
+ if (RD->isDependentType() || RD->isInvalidDecl())
+ return true;
+
+ // The param cannot be passed in registers if CanPassInRegisters is already
+ // set to false.
+ if (!RD->canPassInRegisters())
+ return false;
+
+ if (CCK != TargetInfo::CCK_MicrosoftX86_64)
+ return DestroyedInCallee;
+
+ bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false;
+ bool DtorIsTrivialForCall = false;
+
+ // If a class has at least one non-deleted, trivial copy constructor, it
+ // is passed according to the C ABI. Otherwise, it is passed indirectly.
+ //
+ // Note: This permits classes with non-trivial copy or move ctors to be
+ // passed in registers, so long as they *also* have a trivial copy ctor,
+ // which is non-conforming.
+ if (RD->needsImplicitCopyConstructor()) {
+ if (!RD->defaultedCopyConstructorIsDeleted()) {
+ if (RD->hasTrivialCopyConstructor())
+ CopyCtorIsTrivial = true;
+ if (RD->hasTrivialCopyConstructorForCall())
+ CopyCtorIsTrivialForCall = true;
+ }
+ } else {
+ for (const CXXConstructorDecl *CD : RD->ctors()) {
+ if (CD->isCopyConstructor() && !CD->isDeleted()) {
+ if (CD->isTrivial())
+ CopyCtorIsTrivial = true;
+ if (CD->isTrivialForCall())
+ CopyCtorIsTrivialForCall = true;
+ }
+ }
+ }
+
+ if (RD->needsImplicitDestructor()) {
+ if (!RD->defaultedDestructorIsDeleted() &&
+ RD->hasTrivialDestructorForCall())
+ DtorIsTrivialForCall = true;
+ } else if (const auto *D = RD->getDestructor()) {
+ if (!D->isDeleted() && D->isTrivialForCall())
+ DtorIsTrivialForCall = true;
+ }
+
+ // If the copy ctor and dtor are both trivial-for-calls, pass direct.
+ if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall)
+ return true;
+
+ // If a class has a destructor, we'd really like to pass it indirectly
+ // because it allows us to elide copies. Unfortunately, MSVC makes that
+ // impossible for small types, which it will pass in a single register or
+ // stack slot. Most objects with dtors are large-ish, so handle that early.
+ // We can't call out all large objects as being indirect because there are
+ // multiple x64 calling conventions and the C++ ABI code shouldn't dictate
+ // how we pass large POD types.
+
+ // Note: This permits small classes with nontrivial destructors to be
+ // passed in registers, which is non-conforming.
+ if (CopyCtorIsTrivial &&
+ S.getASTContext().getTypeSize(RD->getTypeForDecl()) <= 64)
+ return true;
+ return false;
+}
+
/// \brief Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
@@ -6001,7 +6081,17 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
checkClassLevelDLLAttribute(Record);
- Record->setCanPassInRegisters(computeCanPassInRegisters(*this, Record));
+ bool ClangABICompat4 =
+ Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
+ TargetInfo::CallingConvKind CCK =
+ Context.getTargetInfo().getCallingConvKind(ClangABICompat4);
+ bool DestroyedInCallee = paramCanBeDestroyedInCallee(*this, Record, CCK);
+
+ if (Record->hasNonTrivialDestructor())
+ Record->setParamDestroyedInCallee(DestroyedInCallee);
+
+ Record->setCanPassInRegisters(
+ computeCanPassInRegister(DestroyedInCallee, Record, CCK, *this));
}
/// Look up the special member function that would be called by a special
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index a90e8657a7d..0c9151cf80a 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -743,6 +743,7 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
RD->setNonTrivialToPrimitiveCopy(Record.readInt());
RD->setNonTrivialToPrimitiveDestroy(Record.readInt());
RD->setCanPassInRegisters(Record.readInt());
+ RD->setParamDestroyedInCallee(Record.readInt());
return Redecl;
}
@@ -4109,6 +4110,7 @@ void ASTDeclReader::UpdateDecl(Decl *D,
OldDD && (OldDD->Definition != RD ||
!Reader.PendingFakeDefinitionData.count(OldDD));
RD->setCanPassInRegisters(Record.readInt());
+ RD->setParamDestroyedInCallee(Record.readInt());
ReadCXXRecordDefinition(RD, /*Update*/true);
// Visible update is handled separately.
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index f2d20ddf3a3..c2aabf59d5e 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5194,6 +5194,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
auto *RD = cast<CXXRecordDecl>(D);
UpdatedDeclContexts.insert(RD->getPrimaryContext());
Record.push_back(RD->canPassInRegisters());
+ Record.push_back(RD->isParamDestroyedInCallee());
Record.AddCXXDefinitionData(RD);
Record.AddOffset(WriteDeclContextLexicalBlock(
*Context, const_cast<CXXRecordDecl *>(RD)));
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index d70debaf85f..254b5175d7b 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -470,6 +470,7 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) {
Record.push_back(D->isNonTrivialToPrimitiveCopy());
Record.push_back(D->isNonTrivialToPrimitiveDestroy());
Record.push_back(D->canPassInRegisters());
+ Record.push_back(D->isParamDestroyedInCallee());
if (D->getDeclContext() == D->getLexicalDeclContext() &&
!D->hasAttrs() &&
@@ -1912,6 +1913,8 @@ void ASTWriter::WriteDeclAbbrevs() {
// isNonTrivialToPrimitiveDestroy
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // canPassInRegisters
+ // isParamDestroyedInCallee
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
// DC
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset
diff --git a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
index f990c274264..36c9ca40db5 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
@@ -172,12 +172,9 @@ void small_arg_with_dtor(SmallWithDtor s) {}
void call_small_arg_with_dtor() {
small_arg_with_dtor(SmallWithDtor());
}
-// The temporary is copied, so it's destroyed in the caller as well as the
-// callee.
// WIN64-LABEL: define dso_local void @"?call_small_arg_with_dtor@@YAXXZ"()
// WIN64: call %struct.SmallWithDtor* @"??0SmallWithDtor@@QEAA@XZ"
// WIN64: call void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %{{.*}})
-// WIN64: call void @"??1SmallWithDtor@@QEAA@XZ"
// WIN64: ret void
// Test that references aren't destroyed in the callee.
diff --git a/clang/test/CodeGenObjCXX/arc-special-member-functions.mm b/clang/test/CodeGenObjCXX/arc-special-member-functions.mm
index af161ddc5ad..278c779481b 100644
--- a/clang/test/CodeGenObjCXX/arc-special-member-functions.mm
+++ b/clang/test/CodeGenObjCXX/arc-special-member-functions.mm
@@ -31,6 +31,8 @@ void test_ObjCMember_copy_construct_destruct(ObjCMember m1) {
void test_ObjCMember_copy_assign(ObjCMember m1, ObjCMember m2) {
// CHECK: {{call.*_ZN10ObjCMemberaSERKS_}}
m1 = m2;
+ // CHECK-NEXT: call void @_ZN10ObjCMemberD1Ev(
+ // CHECK-NEXT: call void @_ZN10ObjCMemberD1Ev(
// CHECK-NEXT: ret void
}
@@ -58,6 +60,8 @@ void test_ObjCArrayMember_copy_construct_destruct(ObjCArrayMember m1) {
void test_ObjCArrayMember_copy_assign(ObjCArrayMember m1, ObjCArrayMember m2) {
// CHECK: {{call.*@_ZN15ObjCArrayMemberaSERKS_}}
m1 = m2;
+ // CHECK-NEXT: call void @_ZN15ObjCArrayMemberD1Ev(
+ // CHECK-NEXT: call void @_ZN15ObjCArrayMemberD1Ev(
// CHECK-NEXT: ret void
}
@@ -79,7 +83,8 @@ void test_ObjCBlockMember_default_construct_destruct() {
void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) {
// CHECK: call void @_ZN15ObjCBlockMemberC1ERKS_
ObjCBlockMember m2 = m1;
- // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev
+ // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
+ // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
// CHECK-NEXT: ret void
}
@@ -87,6 +92,8 @@ void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) {
void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) {
// CHECK: {{call.*_ZN15ObjCBlockMemberaSERKS_}}
m1 = m2;
+ // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
+ // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
// CHECK-NEXT: ret void
}
diff --git a/clang/test/CodeGenObjCXX/trivial_abi.mm b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm
index 6a20578de8d..de8c5f9fcb8 100644
--- a/clang/test/CodeGenObjCXX/trivial_abi.mm
+++ b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm
@@ -1,27 +1,60 @@
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -emit-llvm -o - -DTRIVIALABI %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -fclang-abi-compat=4.0 -emit-llvm -o - -DTRIVIALABI %s | FileCheck %s
+
+// Check that structs consisting solely of __strong or __weak pointer fields are
+// destructed in the callee function and structs consisting solely of __strong
+// pointer fields are passed directly.
// CHECK: %[[STRUCT_STRONGWEAK:.*]] = type { i8*, i8* }
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
// CHECK: %[[STRUCT_S:.*]] = type { i8* }
+// CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* }
+#ifdef TRIVIALABI
struct __attribute__((trivial_abi)) StrongWeak {
+#else
+struct StrongWeak {
+#endif
id fstrong;
__weak id fweak;
};
+#ifdef TRIVIALABI
struct __attribute__((trivial_abi)) Strong {
+#else
+struct Strong {
+#endif
id fstrong;
};
template<class T>
+#ifdef TRIVIALABI
struct __attribute__((trivial_abi)) S {
+#else
+struct S {
+#endif
T a;
};
+struct NonTrivial {
+ NonTrivial();
+ NonTrivial(const NonTrivial &);
+ ~NonTrivial();
+ int *a;
+};
+
+// This struct is not passed directly nor destructed in the callee because f0
+// has type NonTrivial.
+struct ContainsNonTrivial {
+ NonTrivial f0;
+ id f1;
+};
+
// CHECK: define void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %{{.*}})
-// CHECK-NOT: call
-// CHECK: ret void
+// CHECK: call %struct.StrongWeak* @_ZN10StrongWeakD1Ev(
+// CHECK-NEXT: ret void
void testParamStrongWeak(StrongWeak a) {
}
@@ -33,7 +66,7 @@ void testParamStrongWeak(StrongWeak a) {
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGWEAK]]*, %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakC1ERKS_(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]], %[[STRUCT_STRONGWEAK]]* dereferenceable(16) %[[V0]])
// CHECK: call void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
-// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakD1Ev(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
+// CHECK-NOT: call
// CHECK: ret void
void testCallStrongWeak(StrongWeak *a) {
@@ -96,8 +129,23 @@ Strong testReturnStrong(Strong *a) {
}
// CHECK: define void @_Z21testParamWeakTemplate1SIU6__weakP11objc_objectE(%[[STRUCT_S]]* %{{.*}})
+// CHECK: call %struct.S* @_ZN1SIU6__weakP11objc_objectED1Ev(
+// CHECK-NEXT: ret void
+
+void testParamWeakTemplate(S<__weak id> a) {
+}
+
+// CHECK: define void @_Z27testParamContainsNonTrivial18ContainsNonTrivial(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
// CHECK-NOT: call
// CHECK: ret void
-void testParamWeakTemplate(S<__weak id> a) {
+void testParamContainsNonTrivial(ContainsNonTrivial a) {
+}
+
+// CHECK: define void @_Z26testCallContainsNonTrivialP18ContainsNonTrivial(
+// CHECK: call void @_Z27testParamContainsNonTrivial18ContainsNonTrivial(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
+// CHECK: call %struct.ContainsNonTrivial* @_ZN18ContainsNonTrivialD1Ev(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
+
+void testCallContainsNonTrivial(ContainsNonTrivial *a) {
+ testParamContainsNonTrivial(*a);
}
diff --git a/clang/test/CodeGenObjCXX/property-dot-copy-elision.mm b/clang/test/CodeGenObjCXX/property-dot-copy-elision.mm
index 0932ceecd7b..c13a7d9da56 100644
--- a/clang/test/CodeGenObjCXX/property-dot-copy-elision.mm
+++ b/clang/test/CodeGenObjCXX/property-dot-copy-elision.mm
@@ -1,11 +1,13 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -std=c++1z -fobjc-arc -o - %s | FileCheck %s
struct S0 {
+ ~S0();
id f;
};
struct S1 {
S1();
+ ~S1();
S1(S0);
id f;
};
OpenPOWER on IntegriCloud