[Lldb-commits] [clang] [lldb] Reland [MS][clang] Add support for vector deleting destructors (PR #170337)
Mariya Podchishchaeva via lldb-commits
lldb-commits at lists.llvm.org
Tue Dec 2 09:35:20 PST 2025
https://github.com/Fznamznon created https://github.com/llvm/llvm-project/pull/170337
This reverts commit https://github.com/llvm/llvm-project/commit/54a4da9df6906b63878ad6d0ea6da3ed7d2d8432.
MSVC supports an extension allowing to delete an array of objects via pointer whose static type doesn't match its dynamic type. This is done via generation of special destructors - vector deleting destructors. MSVC's virtual tables always contain a pointer to the vector deleting destructor for classes with virtual destructors, so not having this extension implemented causes clang to generate code that is not compatible with the code generated by MSVC, because clang always puts a pointer to a scalar deleting destructor to the vtable. As a bonus the deletion of an array of polymorphic object will work just like it does with MSVC - no memory leaks and correct destructors are called.
This patch will cause clang to emit code that is compatible with code produced by MSVC but not compatible with code produced with clang of older versions, so the new behavior can be disabled via passing -fclang-abi-compat=21 (or lower).
>From 3dc86665b0d5a17cc58eff41d1c2ade80849ffe2 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Mon, 1 Dec 2025 05:29:13 -0800
Subject: [PATCH 1/2] Revert "Revert "Reland [MS][clang] Add support for vector
deleting destructors" (#169116)"
This reverts commit 54a4da9df6906b63878ad6d0ea6da3ed7d2d8432.
---
clang/docs/ReleaseNotes.rst | 8 +
clang/include/clang/AST/ASTContext.h | 22 ++
clang/include/clang/AST/ASTMutationListener.h | 9 +
clang/include/clang/AST/DeclCXX.h | 16 +-
clang/include/clang/AST/VTableBuilder.h | 6 +-
clang/include/clang/Basic/ABI.h | 11 +-
clang/include/clang/Basic/TargetInfo.h | 5 +
clang/include/clang/Sema/Sema.h | 3 +-
clang/include/clang/Serialization/ASTWriter.h | 4 +
clang/lib/AST/ASTContext.cpp | 65 ++++
clang/lib/AST/DeclCXX.cpp | 73 +++-
clang/lib/AST/Expr.cpp | 3 +
clang/lib/AST/ItaniumMangle.cpp | 2 +
clang/lib/AST/MicrosoftMangle.cpp | 22 +-
clang/lib/AST/VTableBuilder.cpp | 18 +-
clang/lib/Basic/TargetInfo.cpp | 7 +
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 7 +
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 5 +-
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 4 +-
clang/lib/CodeGen/CGCXX.cpp | 37 +-
clang/lib/CodeGen/CGCXXABI.cpp | 14 +
clang/lib/CodeGen/CGCXXABI.h | 6 +
clang/lib/CodeGen/CGClass.cpp | 95 ++++-
clang/lib/CodeGen/CGDebugInfo.cpp | 8 +-
clang/lib/CodeGen/CGExprCXX.cpp | 52 ++-
clang/lib/CodeGen/CGVTables.cpp | 4 +-
clang/lib/CodeGen/CodeGenModule.cpp | 50 +++
clang/lib/CodeGen/CodeGenModule.h | 6 +
clang/lib/CodeGen/ItaniumCXXABI.cpp | 5 +-
clang/lib/CodeGen/MicrosoftCXXABI.cpp | 70 +++-
clang/lib/Sema/SemaDeclCXX.cpp | 30 +-
clang/lib/Sema/SemaExprCXX.cpp | 13 +-
clang/lib/Serialization/ASTCommon.h | 4 +-
clang/lib/Serialization/ASTReaderDecl.cpp | 66 +++-
clang/lib/Serialization/ASTWriter.cpp | 36 ++
clang/lib/Serialization/ASTWriterDecl.cpp | 2 +
clang/test/CodeGenCXX/dllexport.cpp | 5 +-
.../microsoft-abi-extern-template.cpp | 2 +-
.../CodeGenCXX/microsoft-abi-structors.cpp | 2 +-
.../test/CodeGenCXX/microsoft-abi-thunks.cpp | 3 +-
.../CodeGenCXX/microsoft-abi-vftables.cpp | 20 +-
.../microsoft-abi-virtual-inheritance.cpp | 17 +-
...multiple-nonvirtual-inheritance-vdtors.cpp | 18 +-
.../microsoft-abi-vtables-return-thunks.cpp | 2 +-
...crosoft-abi-vtables-single-inheritance.cpp | 20 +-
...-vtables-virtual-inheritance-vtordisps.cpp | 30 +-
...rosoft-abi-vtables-virtual-inheritance.cpp | 18 +-
.../CodeGenCXX/microsoft-no-rtti-data.cpp | 2 +-
.../microsoft-vector-deleting-dtors.cpp | 336 ++++++++++++++++++
clang/test/CodeGenCXX/vtable-consteval.cpp | 4 +-
clang/test/DebugInfo/CXX/windows-dtor.cpp | 2 +-
.../module.modulemap | 1 +
.../msvc-vector-deleting-dtors.h | 16 +
.../msvc-vector-deleting-destructors.cpp | 30 ++
clang/test/Modules/vtable-windows.cppm | 2 +-
.../PCH/Inputs/msvc-vector-deleting-dtors.h | 16 +
.../PCH/msvc-vector-deleting-destructors.cpp | 34 ++
clang/test/Profile/cxx-abc-deleting-dtor.cpp | 9 +-
clang/test/SemaCXX/gh134265.cpp | 62 ++++
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 1 +
60 files changed, 1274 insertions(+), 166 deletions(-)
create mode 100644 clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp
create mode 100644 clang/test/Modules/Inputs/msvc-vector-deleting-dtors/module.modulemap
create mode 100644 clang/test/Modules/Inputs/msvc-vector-deleting-dtors/msvc-vector-deleting-dtors.h
create mode 100644 clang/test/Modules/msvc-vector-deleting-destructors.cpp
create mode 100644 clang/test/PCH/Inputs/msvc-vector-deleting-dtors.h
create mode 100644 clang/test/PCH/msvc-vector-deleting-destructors.cpp
create mode 100644 clang/test/SemaCXX/gh134265.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index da064534c25d9..81cdf1627593c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -86,6 +86,12 @@ Potentially Breaking Changes
options-related code has been moved out of the Driver into a separate library.
- The ``clangFrontend`` library no longer depends on ``clangDriver``, which may
break downstream projects that relied on this transitive dependency.
+- Clang now supports MSVC vector deleting destructors when targeting Windows.
+ This means that vtables of classes with virtual destructors will contain a
+ pointer to vector deleting destructor (instead of scalar deleting destructor)
+ which in fact is a different symbol with different name and linkage. This
+ may cause runtime failures if two binaries using the same class defining a
+ virtual destructor are compiled with different versions of clang.
C/C++ Language Potentially Breaking Changes
-------------------------------------------
@@ -625,6 +631,8 @@ Windows Support
- clang-cl now supports /arch:AVX10.1 and /arch:AVX10.2.
- clang-cl now supports /vlen, /vlen=256 and /vlen=512.
+- Clang now supports MSVC vector deleting destructors (GH19772).
+
LoongArch Support
^^^^^^^^^^^^^^^^^
- Enable linker relaxation by default for loongarch64.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 33aa2d343aa7a..6e9e737dcae4f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -370,6 +370,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::DenseSet<const FunctionDecl *> DestroyingOperatorDeletes;
mutable llvm::DenseSet<const FunctionDecl *> TypeAwareOperatorNewAndDeletes;
+ /// Global and array operators delete are only required for MSVC deleting
+ /// destructors support. Store them here to avoid keeping 4 pointers that are
+ /// not always used in each redeclaration of the destructor.
+ mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
+ OperatorDeletesForVirtualDtor;
+ mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
+ GlobalOperatorDeletesForVirtualDtor;
+ mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
+ ArrayOperatorDeletesForVirtualDtor;
+ mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
+ GlobalArrayOperatorDeletesForVirtualDtor;
+
/// The next string literal "version" to allocate during constant evaluation.
/// This is used to distinguish between repeated evaluations of the same
/// string literal.
@@ -3473,6 +3485,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool IsTypeAware);
bool isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const;
+ enum OperatorDeleteKind { Regular, GlobalRegular, Array, ArrayGlobal };
+
+ void addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
+ FunctionDecl *OperatorDelete,
+ OperatorDeleteKind K) const;
+ FunctionDecl *getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
+ OperatorDeleteKind K) const;
+ bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
+ OperatorDeleteKind K) const;
+
/// Retrieve the context for computing mangling numbers in the given
/// DeclContext.
MangleNumberingContext &getManglingNumberContext(const DeclContext *DC);
diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h
index 352af42391782..c8448a25c23a4 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -90,6 +90,15 @@ class ASTMutationListener {
virtual void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *GlobDelete) {}
+ /// A virtual destructor's operator array delete has been resolved.
+ virtual void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *ArrayDelete) {}
+
+ /// A virtual destructor's operator global array delete has been resolved.
+ virtual void
+ ResolvedOperatorGlobArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *GlobArrayDelete) {}
+
/// An implicit member got a definition.
virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index dfa3befb27dd0..5c4ad3c45da19 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -2872,8 +2872,6 @@ class CXXDestructorDecl : public CXXMethodDecl {
// FIXME: Don't allocate storage for these except in the first declaration
// of a virtual destructor.
- FunctionDecl *OperatorDelete = nullptr;
- FunctionDecl *OperatorGlobalDelete = nullptr;
Expr *OperatorDeleteThisArg = nullptr;
CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
@@ -2900,14 +2898,12 @@ class CXXDestructorDecl : public CXXMethodDecl {
void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
void setOperatorGlobalDelete(FunctionDecl *OD);
-
- const FunctionDecl *getOperatorDelete() const {
- return getCanonicalDecl()->OperatorDelete;
- }
-
- const FunctionDecl *getOperatorGlobalDelete() const {
- return getCanonicalDecl()->OperatorGlobalDelete;
- }
+ void setOperatorArrayDelete(FunctionDecl *OD);
+ void setGlobalOperatorArrayDelete(FunctionDecl *OD);
+ const FunctionDecl *getOperatorDelete() const;
+ const FunctionDecl *getOperatorGlobalDelete() const;
+ const FunctionDecl *getArrayOperatorDelete() const;
+ const FunctionDecl *getGlobalArrayOperatorDelete() const;
Expr *getOperatorDeleteThisArg() const {
return getCanonicalDecl()->OperatorDeleteThisArg;
diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h
index a5de41dbc22f1..e1efe8cddcc5e 100644
--- a/clang/include/clang/AST/VTableBuilder.h
+++ b/clang/include/clang/AST/VTableBuilder.h
@@ -150,7 +150,7 @@ class VTableComponent {
bool isRTTIKind() const { return isRTTIKind(getKind()); }
- GlobalDecl getGlobalDecl() const {
+ GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
assert(isUsedFunctionPointerKind() &&
"GlobalDecl can be created only from virtual function");
@@ -161,7 +161,9 @@ class VTableComponent {
case CK_CompleteDtorPointer:
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
case CK_DeletingDtorPointer:
- return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
+ return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
+ ? CXXDtorType::Dtor_VectorDeleting
+ : CXXDtorType::Dtor_Deleting);
case CK_VCallOffset:
case CK_VBaseOffset:
case CK_OffsetToTop:
diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h
index 8279529c316cf..be3edccbf50b2 100644
--- a/clang/include/clang/Basic/ABI.h
+++ b/clang/include/clang/Basic/ABI.h
@@ -32,11 +32,12 @@ enum CXXCtorType {
/// C++ destructor types.
enum CXXDtorType {
- Dtor_Deleting, ///< Deleting dtor
- Dtor_Complete, ///< Complete object dtor
- Dtor_Base, ///< Base object dtor
- Dtor_Comdat, ///< The COMDAT used for dtors
- Dtor_Unified, ///< GCC-style unified dtor
+ Dtor_Deleting, ///< Deleting dtor
+ Dtor_Complete, ///< Complete object dtor
+ Dtor_Base, ///< Base object dtor
+ Dtor_Comdat, ///< The COMDAT used for dtors
+ Dtor_Unified, ///< GCC-style unified dtor
+ Dtor_VectorDeleting, ///< Vector deleting dtor
};
} // end namespace clang
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 1f5932225d31e..655f79a2e6ce1 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1796,6 +1796,11 @@ class TargetInfo : public TransferrableTargetInfo,
/// destructor body.
virtual bool callGlobalDeleteInDeletingDtor(const LangOptions &) const;
+ /// Controls whether to emit MSVC vector deleting destructors. The support for
+ /// vector deleting affects vtable layout and therefore is an ABI breaking
+ /// change. The support was only implemented at Clang 22 timeframe.
+ virtual bool emitVectorDeletingDtors(const LangOptions &) 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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 78ecbccbe4efc..ba921b39a1f63 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8581,7 +8581,8 @@ class Sema final : public SemaBase {
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
CXXRecordDecl *RD,
bool Diagnose,
- bool LookForGlobal);
+ bool LookForGlobal,
+ DeclarationName Name);
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index c77c98dffc39f..dbbfc29058f43 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -955,6 +955,10 @@ class ASTWriter : public ASTDeserializationListener,
Expr *ThisArg) override;
void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
const FunctionDecl *Delete) override;
+ void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *Delete) override;
+ void ResolvedOperatorGlobArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *Delete) override;
void CompletedImplicitDefinition(const FunctionDecl *D) override;
void InstantiationRequested(const ValueDecl *D) override;
void VariableDefinitionInstantiated(const VarDecl *D) override;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b359fc8350375..b929b0fc1aa8e 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13337,6 +13337,71 @@ bool ASTContext::isTypeAwareOperatorNewOrDelete(const FunctionDecl *FD) const {
return TypeAwareOperatorNewAndDeletes.contains(FD->getCanonicalDecl());
}
+void ASTContext::addOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
+ FunctionDecl *OperatorDelete,
+ OperatorDeleteKind K) const {
+ switch (K) {
+ case OperatorDeleteKind::Regular:
+ OperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] = OperatorDelete;
+ break;
+ case OperatorDeleteKind::GlobalRegular:
+ GlobalOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
+ OperatorDelete;
+ break;
+ case OperatorDeleteKind::Array:
+ ArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
+ OperatorDelete;
+ break;
+ case OperatorDeleteKind::ArrayGlobal:
+ GlobalArrayOperatorDeletesForVirtualDtor[Dtor->getCanonicalDecl()] =
+ OperatorDelete;
+ break;
+ }
+}
+
+bool ASTContext::dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
+ OperatorDeleteKind K) const {
+ switch (K) {
+ case OperatorDeleteKind::Regular:
+ return OperatorDeletesForVirtualDtor.contains(Dtor->getCanonicalDecl());
+ case OperatorDeleteKind::GlobalRegular:
+ return GlobalOperatorDeletesForVirtualDtor.contains(
+ Dtor->getCanonicalDecl());
+ case OperatorDeleteKind::Array:
+ return ArrayOperatorDeletesForVirtualDtor.contains(
+ Dtor->getCanonicalDecl());
+ case OperatorDeleteKind::ArrayGlobal:
+ return GlobalArrayOperatorDeletesForVirtualDtor.contains(
+ Dtor->getCanonicalDecl());
+ }
+ return false;
+}
+
+FunctionDecl *
+ASTContext::getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
+ OperatorDeleteKind K) const {
+ const CXXDestructorDecl *Canon = Dtor->getCanonicalDecl();
+ switch (K) {
+ case OperatorDeleteKind::Regular:
+ if (OperatorDeletesForVirtualDtor.contains(Canon))
+ return OperatorDeletesForVirtualDtor[Canon];
+ return nullptr;
+ case OperatorDeleteKind::GlobalRegular:
+ if (GlobalOperatorDeletesForVirtualDtor.contains(Canon))
+ return GlobalOperatorDeletesForVirtualDtor[Canon];
+ return nullptr;
+ case OperatorDeleteKind::Array:
+ if (ArrayOperatorDeletesForVirtualDtor.contains(Canon))
+ return ArrayOperatorDeletesForVirtualDtor[Canon];
+ return nullptr;
+ case OperatorDeleteKind::ArrayGlobal:
+ if (GlobalArrayOperatorDeletesForVirtualDtor.contains(Canon))
+ return GlobalArrayOperatorDeletesForVirtualDtor[Canon];
+ return nullptr;
+ }
+ return nullptr;
+}
+
MangleNumberingContext &
ASTContext::getManglingNumberContext(const DeclContext *DC) {
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 24e4f189cbe4a..c16b1bb7a3453 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3110,12 +3110,15 @@ CXXDestructorDecl *CXXDestructorDecl::Create(
}
void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
- auto *First = cast<CXXDestructorDecl>(getFirstDecl());
- if (OD && !First->OperatorDelete) {
- First->OperatorDelete = OD;
- First->OperatorDeleteThisArg = ThisArg;
+ assert(!OD || (OD->getDeclName().getCXXOverloadedOperator() == OO_Delete));
+ if (OD && !getASTContext().dtorHasOperatorDelete(
+ this, ASTContext::OperatorDeleteKind::Regular)) {
+ getASTContext().addOperatorDeleteForVDtor(
+ this, OD, ASTContext::OperatorDeleteKind::Regular);
+ getCanonicalDecl()->OperatorDeleteThisArg = ThisArg;
if (auto *L = getASTMutationListener())
- L->ResolvedOperatorDelete(First, OD, ThisArg);
+ L->ResolvedOperatorDelete(cast<CXXDestructorDecl>(getCanonicalDecl()), OD,
+ ThisArg);
}
}
@@ -3127,14 +3130,63 @@ void CXXDestructorDecl::setOperatorGlobalDelete(FunctionDecl *OD) {
assert(!OD ||
(OD->getDeclName().getCXXOverloadedOperator() == OO_Delete &&
OD->getDeclContext()->getRedeclContext()->isTranslationUnit()));
- auto *Canonical = cast<CXXDestructorDecl>(getCanonicalDecl());
- if (!Canonical->OperatorGlobalDelete) {
- Canonical->OperatorGlobalDelete = OD;
+ if (OD && !getASTContext().dtorHasOperatorDelete(
+ this, ASTContext::OperatorDeleteKind::GlobalRegular)) {
+ getASTContext().addOperatorDeleteForVDtor(
+ this, OD, ASTContext::OperatorDeleteKind::GlobalRegular);
if (auto *L = getASTMutationListener())
- L->ResolvedOperatorGlobDelete(Canonical, OD);
+ L->ResolvedOperatorGlobDelete(cast<CXXDestructorDecl>(getCanonicalDecl()),
+ OD);
}
}
+void CXXDestructorDecl::setOperatorArrayDelete(FunctionDecl *OD) {
+ assert(!OD ||
+ (OD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete));
+ if (OD && !getASTContext().dtorHasOperatorDelete(
+ this, ASTContext::OperatorDeleteKind::Array)) {
+ getASTContext().addOperatorDeleteForVDtor(
+ this, OD, ASTContext::OperatorDeleteKind::Array);
+ if (auto *L = getASTMutationListener())
+ L->ResolvedOperatorArrayDelete(
+ cast<CXXDestructorDecl>(getCanonicalDecl()), OD);
+ }
+}
+
+void CXXDestructorDecl::setGlobalOperatorArrayDelete(FunctionDecl *OD) {
+ assert(!OD ||
+ (OD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete &&
+ OD->getDeclContext()->getRedeclContext()->isTranslationUnit()));
+ if (OD && !getASTContext().dtorHasOperatorDelete(
+ this, ASTContext::OperatorDeleteKind::ArrayGlobal)) {
+ getASTContext().addOperatorDeleteForVDtor(
+ this, OD, ASTContext::OperatorDeleteKind::ArrayGlobal);
+ if (auto *L = getASTMutationListener())
+ L->ResolvedOperatorGlobArrayDelete(
+ cast<CXXDestructorDecl>(getCanonicalDecl()), OD);
+ }
+}
+
+const FunctionDecl *CXXDestructorDecl::getOperatorDelete() const {
+ return getASTContext().getOperatorDeleteForVDtor(
+ this, ASTContext::OperatorDeleteKind::Regular);
+}
+
+const FunctionDecl *CXXDestructorDecl::getOperatorGlobalDelete() const {
+ return getASTContext().getOperatorDeleteForVDtor(
+ this, ASTContext::OperatorDeleteKind::GlobalRegular);
+}
+
+const FunctionDecl *CXXDestructorDecl::getArrayOperatorDelete() const {
+ return getASTContext().getOperatorDeleteForVDtor(
+ this, ASTContext::OperatorDeleteKind::Array);
+}
+
+const FunctionDecl *CXXDestructorDecl::getGlobalArrayOperatorDelete() const {
+ return getASTContext().getOperatorDeleteForVDtor(
+ this, ASTContext::OperatorDeleteKind::ArrayGlobal);
+}
+
bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
// C++20 [expr.delete]p6: If the value of the operand of the delete-
// expression is not a null pointer value and the selected deallocation
@@ -3146,7 +3198,8 @@ bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
// delete operator, as that destructor is never called, unless the
// destructor is virtual (see [expr.delete]p8.1) because then the
// selected operator depends on the dynamic type of the pointer.
- const FunctionDecl *SelectedOperatorDelete = OpDel ? OpDel : OperatorDelete;
+ const FunctionDecl *SelectedOperatorDelete =
+ OpDel ? OpDel : getOperatorDelete();
if (!SelectedOperatorDelete)
return true;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 1f405920ce6b5..e29967adbb09f 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -71,6 +71,9 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const {
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
DerivedType = PTy->getPointeeType();
+ while (const ArrayType *ATy = DerivedType->getAsArrayTypeUnsafe())
+ DerivedType = ATy->getElementType();
+
if (DerivedType->isDependentType())
return nullptr;
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 5572e0a7ae59c..a5bcf5c97e837 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6040,6 +6040,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
case Dtor_Comdat:
Out << "D5";
break;
+ case Dtor_VectorDeleting:
+ llvm_unreachable("Itanium ABI does not use vector deleting dtors");
}
}
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index f1baf9f49384b..551aa7bf3321c 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1492,8 +1492,9 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
// <operator-name> ::= ?_G # scalar deleting destructor
case Dtor_Deleting: Out << "?_G"; return;
// <operator-name> ::= ?_E # vector deleting destructor
- // FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need
- // it.
+ case Dtor_VectorDeleting:
+ Out << "?_E";
+ return;
case Dtor_Comdat:
llvm_unreachable("not expecting a COMDAT");
case Dtor_Unified:
@@ -2913,9 +2914,12 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
// ::= @ # structors (they have no declared return type)
if (IsStructor) {
if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
- // The scalar deleting destructor takes an extra int argument which is not
- // reflected in the AST.
- if (StructorType == Dtor_Deleting) {
+ // The deleting destructors take an extra argument of type int that
+ // indicates whether the storage for the object should be deleted and
+ // whether a single object or an array of objects is being destroyed. This
+ // extra argument is not reflected in the AST.
+ if (StructorType == Dtor_Deleting ||
+ StructorType == Dtor_VectorDeleting) {
Out << (PointersAre64Bit ? "PEAXI at Z" : "PAXI at Z");
return;
}
@@ -3911,10 +3915,10 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
const ThunkInfo &Thunk,
bool /*ElideOverrideInfo*/,
raw_ostream &Out) {
- // FIXME: Actually, the dtor thunk should be emitted for vector deleting
- // dtors rather than scalar deleting dtors. Just use the vector deleting dtor
- // mangling manually until we support both deleting dtor types.
- assert(Type == Dtor_Deleting);
+ // The dtor thunk should use vector deleting dtor mangling, however as an
+ // optimization we may end up emitting only scalar deleting dtor body, so just
+ // use the vector deleting dtor mangling manually.
+ assert(Type == Dtor_Deleting || Type == Dtor_VectorDeleting);
msvc_hashing_ostream MHO(Out);
MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type);
Mangler.getStream() << "??_E";
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index 3ded3a51206da..9951126c2c3a3 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -2658,7 +2658,12 @@ class VFTableBuilder {
MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.getVBaseWithVPtr(),
WhichVFPtr.NonVirtualOffset, MI.VFTableIndex);
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
- MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
+ // In Microsoft ABI vftable always references vector deleting dtor.
+ CXXDtorType DtorTy = Context.getTargetInfo().emitVectorDeletingDtors(
+ Context.getLangOpts())
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting;
+ MethodVFTableLocations[GlobalDecl(DD, DtorTy)] = Loc;
} else {
MethodVFTableLocations[MD] = Loc;
}
@@ -3288,7 +3293,11 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
const CXXDestructorDecl *DD = Component.getDestructorDecl();
DD->printQualifiedName(Out);
- Out << "() [scalar deleting]";
+ if (Context.getTargetInfo().emitVectorDeletingDtors(
+ Context.getLangOpts()))
+ Out << "() [vector deleting]";
+ else
+ Out << "() [scalar deleting]";
if (DD->isPureVirtual())
Out << " [pure]";
@@ -3758,7 +3767,7 @@ void MicrosoftVTableContext::dumpMethodLocations(
PredefinedIdentKind::PrettyFunctionNoVirtual, MD);
if (isa<CXXDestructorDecl>(MD)) {
- IndicesMap[I.second] = MethodName + " [scalar deleting]";
+ IndicesMap[I.second] = MethodName + " [vector deleting]";
} else {
IndicesMap[I.second] = MethodName;
}
@@ -3874,7 +3883,8 @@ MicrosoftVTableContext::getMethodVFTableLocation(GlobalDecl GD) {
assert(hasVtableSlot(cast<CXXMethodDecl>(GD.getDecl())) &&
"Only use this method for virtual methods or dtors");
if (isa<CXXDestructorDecl>(GD.getDecl()))
- assert(GD.getDtorType() == Dtor_Deleting);
+ assert(GD.getDtorType() == Dtor_VectorDeleting ||
+ GD.getDtorType() == Dtor_Deleting);
GD = GD.getCanonicalDecl();
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index c0ed900ebd45c..0d7eb0a356bab 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -636,6 +636,13 @@ bool TargetInfo::callGlobalDeleteInDeletingDtor(
return false;
}
+bool TargetInfo::emitVectorDeletingDtors(const LangOptions &LangOpts) const {
+ if (getCXXABI() == TargetCXXABI::Microsoft &&
+ LangOpts.getClangABICompat() > LangOptions::ClangABI::Ver21)
+ return true;
+ return false;
+}
+
bool TargetInfo::areDefaultedSMFStillPOD(const LangOptions &LangOpts) const {
return LangOpts.getClangABICompat() > LangOptions::ClangABI::Ver15;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 81fd8ee106107..bac44cecd54bb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -777,6 +777,13 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) {
deleteTy = getContext().getBaseElementType(deleteTy);
ptr = ptr.withElementType(builder, convertTypeForMem(deleteTy));
+ if (e->isArrayForm() &&
+ cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors(
+ cgm.getASTContext().getLangOpts())) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCXXDeleteExpr: emitVectorDeletingDtors");
+ }
+
if (e->isArrayForm()) {
assert(!cir::MissingFeatures::deleteArray());
cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: array delete");
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 22128ed3521f8..b656f44b3d33a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -754,7 +754,9 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
// outside of the function-try-block, which means it's always
// possible to delegate the destructor body to the complete
// destructor. Do so.
- if (dtorType == Dtor_Deleting) {
+ if (dtorType == Dtor_Deleting || dtorType == Dtor_VectorDeleting) {
+ if (cxxStructorImplicitParamValue && dtorType == Dtor_VectorDeleting)
+ cgm.errorNYI(dtor->getSourceRange(), "emitConditionalArrayDtorCall");
RunCleanupsScope dtorEpilogue(*this);
enterDtorCleanups(dtor, Dtor_Deleting);
if (haveInsertPoint()) {
@@ -787,6 +789,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
case Dtor_Comdat:
llvm_unreachable("not expecting a COMDAT");
case Dtor_Deleting:
+ case Dtor_VectorDeleting:
llvm_unreachable("already handled deleting case");
case Dtor_Complete:
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 36bab625c4dd2..301954405027b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -145,7 +145,9 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
case VTableComponent::CK_FunctionPointer:
case VTableComponent::CK_CompleteDtorPointer:
case VTableComponent::CK_DeletingDtorPointer: {
- GlobalDecl gd = component.getGlobalDecl();
+ GlobalDecl gd = component.getGlobalDecl(
+ cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors(
+ cgm.getASTContext().getLangOpts()));
assert(!cir::MissingFeatures::cudaSupport());
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index 59aeff6804b61..8ca53c1b58a9d 100644
--- a/clang/lib/CodeGen/CGCXX.cpp
+++ b/clang/lib/CodeGen/CGCXX.cpp
@@ -174,7 +174,6 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// requires explicit comdat support in the IL.
if (llvm::GlobalValue::isWeakForLinker(TargetLinkage))
return true;
-
// Create the alias with no name.
auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
Aliasee, &getModule());
@@ -200,6 +199,42 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
return false;
}
+/// Emit a definition as a global alias for another definition, unconditionally.
+void CodeGenModule::EmitDefinitionAsAlias(GlobalDecl AliasDecl,
+ GlobalDecl TargetDecl) {
+
+ llvm::Type *AliasValueType = getTypes().GetFunctionType(AliasDecl);
+
+ StringRef MangledName = getMangledName(AliasDecl);
+ llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
+ if (Entry && !Entry->isDeclaration())
+ return;
+ auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl));
+
+ // Determine the linkage type for the alias.
+ llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl);
+
+ // Create the alias with no name.
+ auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
+ Aliasee, &getModule());
+ // Destructors are always unnamed_addr.
+ Alias->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+
+ if (Entry) {
+ assert(Entry->getValueType() == AliasValueType &&
+ Entry->getAddressSpace() == Alias->getAddressSpace() &&
+ "declaration exists with different type");
+ Alias->takeName(Entry);
+ Entry->replaceAllUsesWith(Alias);
+ Entry->eraseFromParent();
+ } else {
+ Alias->setName(MangledName);
+ }
+
+ // Set any additional necessary attributes for the alias.
+ SetCommonAttributes(AliasDecl, Alias);
+}
+
llvm::Function *CodeGenModule::codegenCXXStructor(GlobalDecl GD) {
const CGFunctionInfo &FnInfo = getTypes().arrangeCXXStructorDeclaration(GD);
auto *Fn = cast<llvm::Function>(
diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp
index 30e5dc2b6cbd9..4051cacbbbc1d 100644
--- a/clang/lib/CodeGen/CGCXXABI.cpp
+++ b/clang/lib/CodeGen/CGCXXABI.cpp
@@ -268,6 +268,20 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
}
+void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
+ QualType eltTy, llvm::Value *&numElements,
+ llvm::Value *&allocPtr, CharUnits &cookieSize) {
+ assert(eltTy.isDestructedType());
+
+ // Derive a char* in the same address space as the pointer.
+ ptr = ptr.withElementType(CGF.Int8Ty);
+
+ cookieSize = getArrayCookieSizeImpl(eltTy);
+ Address allocAddr = CGF.Builder.CreateConstInBoundsByteGEP(ptr, -cookieSize);
+ allocPtr = allocAddr.emitRawPointer(CGF);
+ numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
+}
+
llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
Address ptr,
CharUnits cookieSize) {
diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index 2dd320dbda976..47090276c56b0 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -583,6 +583,12 @@ class CGCXXABI {
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
+ /// Reads the array cookie associated with the given pointer,
+ /// that should have one.
+ void ReadArrayCookie(CodeGenFunction &CGF, Address Ptr, QualType ElementType,
+ llvm::Value *&NumElements, llvm::Value *&AllocPtr,
+ CharUnits &CookieSize);
+
/// Return whether the given global decl needs a VTT parameter.
virtual bool NeedsVTTParameter(GlobalDecl GD);
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index f782b0cd17da4..ced175a9a8f0e 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1442,6 +1442,95 @@ static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
return true;
}
+static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
+ CodeGenFunction &CGF,
+ llvm::Value *ShouldDeleteCondition) {
+ Address ThisPtr = CGF.LoadCXXThisAddress();
+ llvm::BasicBlock *ScalarBB = CGF.createBasicBlock("dtor.scalar");
+ llvm::BasicBlock *callDeleteBB =
+ CGF.createBasicBlock("dtor.call_delete_after_array_destroy");
+ llvm::BasicBlock *VectorBB = CGF.createBasicBlock("dtor.vector");
+ auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
+ llvm::Value *CheckTheBitForArrayDestroy = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 2));
+ llvm::Value *ShouldDestroyArray =
+ CGF.Builder.CreateIsNull(CheckTheBitForArrayDestroy);
+ CGF.Builder.CreateCondBr(ShouldDestroyArray, ScalarBB, VectorBB);
+
+ CGF.EmitBlock(VectorBB);
+
+ llvm::Value *numElements = nullptr;
+ llvm::Value *allocatedPtr = nullptr;
+ CharUnits cookieSize;
+ QualType EltTy = DD->getThisType()->getPointeeType();
+ CGF.CGM.getCXXABI().ReadArrayCookie(CGF, ThisPtr, EltTy, numElements,
+ allocatedPtr, cookieSize);
+
+ // Destroy the elements.
+ QualType::DestructionKind dtorKind = EltTy.isDestructedType();
+
+ assert(dtorKind);
+ assert(numElements && "no element count for a type with a destructor!");
+
+ CharUnits elementSize = CGF.getContext().getTypeSizeInChars(EltTy);
+ CharUnits elementAlign =
+ ThisPtr.getAlignment().alignmentOfArrayElement(elementSize);
+
+ llvm::Value *arrayBegin = ThisPtr.emitRawPointer(CGF);
+ llvm::Value *arrayEnd = CGF.Builder.CreateInBoundsGEP(
+ ThisPtr.getElementType(), arrayBegin, numElements, "delete.end");
+
+ // We already checked that the array is not 0-length before entering vector
+ // deleting dtor.
+ CGF.emitArrayDestroy(arrayBegin, arrayEnd, EltTy, elementAlign,
+ CGF.getDestroyer(dtorKind),
+ /*checkZeroLength*/ false, CGF.needsEHCleanup(dtorKind));
+
+ llvm::BasicBlock *VectorBBCont = CGF.createBasicBlock("dtor.vector.cont");
+ CGF.EmitBlock(VectorBBCont);
+
+ llvm::Value *CheckTheBitForDeleteCall = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
+
+ llvm::Value *ShouldCallDelete =
+ CGF.Builder.CreateIsNull(CheckTheBitForDeleteCall);
+ CGF.Builder.CreateCondBr(ShouldCallDelete, CGF.ReturnBlock.getBlock(),
+ callDeleteBB);
+ CGF.EmitBlock(callDeleteBB);
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
+ const CXXRecordDecl *ClassDecl = Dtor->getParent();
+ assert(Dtor->getArrayOperatorDelete());
+ if (!Dtor->getGlobalArrayOperatorDelete()) {
+ CGF.EmitDeleteCall(Dtor->getArrayOperatorDelete(), allocatedPtr,
+ CGF.getContext().getCanonicalTagType(ClassDecl));
+ } else {
+ // If global operator[] is set, the class had its own operator delete[].
+ // In that case, check the 4th bit. If it is set, we need to call
+ // ::delete[].
+ llvm::Value *CheckTheBitForGlobDeleteCall = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 4));
+
+ llvm::Value *ShouldCallGlobDelete =
+ CGF.Builder.CreateIsNull(CheckTheBitForGlobDeleteCall);
+ llvm::BasicBlock *GlobDelete =
+ CGF.createBasicBlock("dtor.call_glob_delete_after_array_destroy");
+ llvm::BasicBlock *ClassDelete =
+ CGF.createBasicBlock("dtor.call_class_delete_after_array_destroy");
+ CGF.Builder.CreateCondBr(ShouldCallGlobDelete, ClassDelete, GlobDelete);
+ CGF.EmitBlock(ClassDelete);
+ CGF.EmitDeleteCall(Dtor->getArrayOperatorDelete(), allocatedPtr,
+ CGF.getContext().getCanonicalTagType(ClassDecl));
+ CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+
+ CGF.EmitBlock(GlobDelete);
+ CGF.EmitDeleteCall(Dtor->getGlobalArrayOperatorDelete(), allocatedPtr,
+ CGF.getContext().getCanonicalTagType(ClassDecl));
+ }
+
+ CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+ CGF.EmitBlock(ScalarBB);
+}
+
/// EmitDestructorBody - Emits the body of the current destructor.
void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
@@ -1471,7 +1560,9 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// outside of the function-try-block, which means it's always
// possible to delegate the destructor body to the complete
// destructor. Do so.
- if (DtorType == Dtor_Deleting) {
+ if (DtorType == Dtor_Deleting || DtorType == Dtor_VectorDeleting) {
+ if (CXXStructorImplicitParamValue && DtorType == Dtor_VectorDeleting)
+ EmitConditionalArrayDtorCall(Dtor, *this, CXXStructorImplicitParamValue);
RunCleanupsScope DtorEpilogue(*this);
EnterDtorCleanups(Dtor, Dtor_Deleting);
if (HaveInsertPoint()) {
@@ -1502,6 +1593,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
llvm_unreachable("not expecting a unified dtor");
case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
case Dtor_Deleting: llvm_unreachable("already handled deleting case");
+ case Dtor_VectorDeleting:
+ llvm_unreachable("already handled vector deleting case");
case Dtor_Complete:
assert((Body || getTarget().getCXXABI().isMicrosoft()) &&
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index c50f372c1f331..ec3d80cc18e04 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2363,7 +2363,13 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
// Emit MS ABI vftable information. There is only one entry for the
// deleting dtor.
const auto *DD = dyn_cast<CXXDestructorDecl>(Method);
- GlobalDecl GD = DD ? GlobalDecl(DD, Dtor_Deleting) : GlobalDecl(Method);
+ GlobalDecl GD =
+ DD ? GlobalDecl(
+ DD, CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts())
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting)
+ : GlobalDecl(Method);
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
VIndex = ML.Index;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 14d8db32bafc6..f64cf9f8a6c2d 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1206,6 +1206,16 @@ void CodeGenFunction::EmitNewArrayInitializer(
EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, CCE,
/*NewPointerIsChecked*/true,
CCE->requiresZeroInitialization());
+
+ // For MSVC vector deleting destructors support we record that for the class
+ // new[] was called. We try to optimize the code size and only emit vector
+ // deleting destructors when they are required. Vector deleting destructors
+ // are required for delete[] call but MSVC triggers emission of them
+ // whenever new[] is called for an object of the class and we do the same
+ // for compatibility.
+ if (CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts()))
+ CGM.requireVectorDestructorDefinition(Ctor->getParent());
return;
}
@@ -1912,10 +1922,8 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
/// Emit the code for deleting a single object.
/// \return \c true if we started emitting UnconditionalDeleteBlock, \c false
/// if not.
-static bool EmitObjectDelete(CodeGenFunction &CGF,
- const CXXDeleteExpr *DE,
- Address Ptr,
- QualType ElementType,
+static bool EmitObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
+ Address Ptr, QualType ElementType,
llvm::BasicBlock *UnconditionalDeleteBlock) {
// C++11 [expr.delete]p3:
// If the static type of the object to be deleted is different from its
@@ -2109,6 +2117,42 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
DeleteTy = getContext().getBaseElementType(DeleteTy);
Ptr = Ptr.withElementType(ConvertTypeForMem(DeleteTy));
+ if (E->isArrayForm() &&
+ CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts())) {
+ if (auto *RD = DeleteTy->getAsCXXRecordDecl()) {
+ auto *Dtor = RD->getDestructor();
+ if (Dtor && Dtor->isVirtual()) {
+ llvm::Value *NumElements = nullptr;
+ llvm::Value *AllocatedPtr = nullptr;
+ CharUnits CookieSize;
+ llvm::BasicBlock *BodyBB = createBasicBlock("vdtor.call");
+ llvm::BasicBlock *DoneBB = createBasicBlock("vdtor.nocall");
+ // Check array cookie to see if the array has length 0. Don't call
+ // the destructor in that case.
+ CGM.getCXXABI().ReadArrayCookie(*this, Ptr, E, DeleteTy, NumElements,
+ AllocatedPtr, CookieSize);
+
+ auto *CondTy = cast<llvm::IntegerType>(NumElements->getType());
+ llvm::Value *IsEmpty = Builder.CreateICmpEQ(
+ NumElements, llvm::ConstantInt::get(CondTy, 0));
+ Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
+
+ // Delete cookie for empty array.
+ const FunctionDecl *OperatorDelete = E->getOperatorDelete();
+ EmitBlock(DoneBB);
+ EmitDeleteCall(OperatorDelete, AllocatedPtr, DeleteTy, NumElements,
+ CookieSize);
+ EmitBranch(DeleteEnd);
+
+ EmitBlock(BodyBB);
+ if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
+ EmitBlock(DeleteEnd);
+ return;
+ }
+ }
+ }
+
if (E->isArrayForm()) {
EmitArrayDelete(*this, E, Ptr, DeleteTy);
EmitBlock(DeleteEnd);
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 3fbac308a9178..c95bd9a3067a9 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -775,7 +775,9 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
case VTableComponent::CK_FunctionPointer:
case VTableComponent::CK_CompleteDtorPointer:
case VTableComponent::CK_DeletingDtorPointer: {
- GlobalDecl GD = component.getGlobalDecl();
+ GlobalDecl GD = component.getGlobalDecl(
+ CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts()));
const bool IsThunk =
nextVTableThunkIndex < layout.vtable_thunks().size() &&
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4789c6b26797f..ddfba1a9a3734 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -8323,3 +8323,53 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) {
NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx);
}
+
+bool CodeGenModule::classNeedsVectorDestructor(const CXXRecordDecl *RD) {
+ if (!Context.getTargetInfo().emitVectorDeletingDtors(Context.getLangOpts()))
+ return false;
+ CXXDestructorDecl *Dtor = RD->getDestructor();
+ // The compiler can't know if new[]/delete[] will be used outside of the DLL,
+ // so just force vector deleting destructor emission if dllexport is present.
+ // This matches MSVC behavior.
+ if (Dtor && Dtor->isVirtual() && Dtor->isDefined() &&
+ Dtor->hasAttr<DLLExportAttr>())
+ return true;
+
+ return RequireVectorDeletingDtor.count(RD);
+}
+
+void CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
+ if (!Context.getTargetInfo().emitVectorDeletingDtors(Context.getLangOpts()))
+ return;
+ RequireVectorDeletingDtor.insert(RD);
+
+ // To reduce code size in general case we lazily emit scalar deleting
+ // destructor definition and an alias from vector deleting destructor to
+ // scalar deleting destructor. It may happen that we first emitted the scalar
+ // deleting destructor definition and the alias and then discovered that the
+ // definition of the vector deleting destructor is required. Then we need to
+ // remove the alias and the scalar deleting destructor and queue vector
+ // deleting destructor body for emission. Check if that is the case.
+ CXXDestructorDecl *DtorD = RD->getDestructor();
+ GlobalDecl ScalarDtorGD(DtorD, Dtor_Deleting);
+ StringRef MangledName = getMangledName(ScalarDtorGD);
+ llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
+ if (Entry && !Entry->isDeclaration()) {
+ GlobalDecl VectorDtorGD(DtorD, Dtor_VectorDeleting);
+ StringRef VDName = getMangledName(VectorDtorGD);
+ llvm::GlobalValue *VDEntry = GetGlobalValue(VDName);
+ // It exists and it should be an alias.
+ assert(VDEntry && isa<llvm::GlobalAlias>(VDEntry));
+ auto *NewFn = llvm::Function::Create(
+ cast<llvm::FunctionType>(VDEntry->getValueType()),
+ llvm::Function::ExternalLinkage, VDName, &getModule());
+ SetFunctionAttributes(VectorDtorGD, NewFn, /*IsIncompleteFunction*/ false,
+ /*IsThunk*/ false);
+ NewFn->takeName(VDEntry);
+ VDEntry->replaceAllUsesWith(NewFn);
+ VDEntry->eraseFromParent();
+ Entry->replaceAllUsesWith(NewFn);
+ Entry->eraseFromParent();
+ addDeferredDeclToEmit(VectorDtorGD);
+ }
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index a253bcda2d06c..2acfc83338a0c 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -529,6 +529,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// that we don't re-emit the initializer.
llvm::DenseMap<const Decl*, unsigned> DelayedCXXInitPosition;
+ /// To remember which types did require a vector deleting dtor.
+ llvm::SmallPtrSet<const CXXRecordDecl *, 16> RequireVectorDeletingDtor;
+
typedef std::pair<OrderGlobalInitsOrStermFinalizers, llvm::Function *>
GlobalInitData;
@@ -1547,6 +1550,7 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitGlobal(GlobalDecl D);
bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D);
+ void EmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target);
llvm::GlobalValue *GetGlobalValue(StringRef Ref);
@@ -1824,6 +1828,8 @@ class CodeGenModule : public CodeGenTypeCache {
// behavior. So projects like the Linux kernel can rely on it.
return !getLangOpts().CPlusPlus;
}
+ void requireVectorDestructorDefinition(const CXXRecordDecl *RD);
+ bool classNeedsVectorDestructor(const CXXRecordDecl *RD);
// Helper to get the alignment for a variable.
unsigned getVtableGlobalVarAlignment(const VarDecl *D = nullptr) {
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 65c47633bc5c4..82a0acb9cd51e 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -93,6 +93,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
llvm_unreachable("emitting dtor comdat as function?");
case Dtor_Unified:
llvm_unreachable("emitting unified dtor as function?");
+ case Dtor_VectorDeleting:
+ llvm_unreachable("unexpected dtor kind for this ABI");
}
llvm_unreachable("bad dtor kind");
}
@@ -458,7 +460,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
if (!IsInlined)
continue;
- StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl());
+ StringRef Name = CGM.getMangledName(
+ VtableComponent.getGlobalDecl(/*HasVectorDeletingDtors=*/false));
auto *Entry = CGM.GetGlobalValue(Name);
// This checks if virtual inline function has already been emitted.
// Note that it is possible that this inline function would be emitted
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 71e24491f19a4..11ca94f03cb98 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -71,8 +71,8 @@ class MicrosoftCXXABI : public CGCXXABI {
switch (GD.getDtorType()) {
case Dtor_Complete:
case Dtor_Deleting:
+ case Dtor_VectorDeleting:
return true;
-
case Dtor_Base:
return false;
@@ -269,7 +269,11 @@ class MicrosoftCXXABI : public CGCXXABI {
// There's only Dtor_Deleting in vftable but it shares the this
// adjustment with the base one, so look up the deleting one instead.
- LookupGD = GlobalDecl(DD, Dtor_Deleting);
+ LookupGD = GlobalDecl(
+ DD, CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts())
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting);
}
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -351,8 +355,9 @@ class MicrosoftCXXABI : public CGCXXABI {
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
CallArgList &CallArgs) override {
- assert(GD.getDtorType() == Dtor_Deleting &&
- "Only deleting destructor thunks are available in this ABI");
+ assert((GD.getDtorType() == Dtor_VectorDeleting ||
+ GD.getDtorType() == Dtor_Deleting) &&
+ "Only vector deleting destructor thunks are available in this ABI");
CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
getContext().IntTy);
}
@@ -1107,7 +1112,8 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
static bool isDeletingDtor(GlobalDecl GD) {
return isa<CXXDestructorDecl>(GD.getDecl()) &&
- GD.getDtorType() == Dtor_Deleting;
+ (GD.getDtorType() == Dtor_Deleting ||
+ GD.getDtorType() == Dtor_VectorDeleting);
}
bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
@@ -1360,7 +1366,8 @@ MicrosoftCXXABI::buildStructorSignature(GlobalDecl GD,
AddedStructorArgCounts Added;
// TODO: 'for base' flag
if (isa<CXXDestructorDecl>(GD.getDecl()) &&
- GD.getDtorType() == Dtor_Deleting) {
+ (GD.getDtorType() == Dtor_Deleting ||
+ GD.getDtorType() == Dtor_VectorDeleting)) {
// The scalar deleting destructor takes an implicit int parameter.
ArgTys.push_back(getContext().IntTy);
++Added.Suffix;
@@ -1392,7 +1399,7 @@ void MicrosoftCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
CXXDtorType DT) const {
// Deleting destructor variants are never imported or exported. Give them the
// default storage class.
- if (DT == Dtor_Deleting) {
+ if (DT == Dtor_Deleting || DT == Dtor_VectorDeleting) {
GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
} else {
const NamedDecl *ND = Dtor;
@@ -1428,6 +1435,12 @@ llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
return llvm::GlobalValue::LinkOnceODRLinkage;
case Dtor_Unified:
llvm_unreachable("MS C++ ABI does not support unified dtors");
+ case Dtor_VectorDeleting:
+ // Use the weak, non-ODR linkage for vector deleting destructors to block
+ // inlining. This enables an MS ABI code-size saving optimization that
+ // allows us to avoid emitting array deletion code when arrays of a given
+ // type are not allocated within the final linkage unit.
+ return llvm::GlobalValue::WeakAnyLinkage;
case Dtor_Comdat:
llvm_unreachable("MS C++ ABI does not support comdat dtors");
}
@@ -1459,7 +1472,11 @@ MicrosoftCXXABI::getVirtualFunctionPrologueThisAdjustment(GlobalDecl GD) {
// There's no Dtor_Base in vftable but it shares the this adjustment with
// the deleting one, so look it up instead.
- GD = GlobalDecl(DD, Dtor_Deleting);
+ GD =
+ GlobalDecl(DD, CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts())
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting);
}
MethodVFTableLocation ML =
@@ -1508,7 +1525,11 @@ Address MicrosoftCXXABI::adjustThisArgumentForVirtualFunctionCall(
// There's only Dtor_Deleting in vftable but it shares the this adjustment
// with the base one, so look up the deleting one instead.
- LookupGD = GlobalDecl(DD, Dtor_Deleting);
+ LookupGD =
+ GlobalDecl(DD, CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
+ CGM.getContext().getLangOpts())
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting);
}
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -2018,24 +2039,30 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
auto *CE = dyn_cast<const CXXMemberCallExpr *>(E);
auto *D = dyn_cast<const CXXDeleteExpr *>(E);
assert((CE != nullptr) ^ (D != nullptr));
- assert(CE == nullptr || CE->arguments().empty());
- assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
+ assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
+ assert(DtorType == Dtor_VectorDeleting || DtorType == Dtor_Complete ||
+ DtorType == Dtor_Deleting);
// We have only one destructor in the vftable but can get both behaviors
// by passing an implicit int parameter.
- GlobalDecl GD(Dtor, Dtor_Deleting);
+ ASTContext &Context = getContext();
+ bool VectorDeletingDtorsEnabled =
+ Context.getTargetInfo().emitVectorDeletingDtors(Context.getLangOpts());
+ GlobalDecl GD(Dtor, VectorDeletingDtorsEnabled ? Dtor_VectorDeleting
+ : Dtor_Deleting);
const CGFunctionInfo *FInfo =
&CGM.getTypes().arrangeCXXStructorDeclaration(GD);
llvm::FunctionType *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
CGCallee Callee = CGCallee::forVirtual(CE, GD, This, Ty);
- ASTContext &Context = getContext();
bool IsDeleting = DtorType == Dtor_Deleting;
+ bool IsArrayDelete = D && D->isArrayForm() && VectorDeletingDtorsEnabled;
bool IsGlobalDelete = D && D->isGlobalDelete() &&
Context.getTargetInfo().callGlobalDeleteInDeletingDtor(
Context.getLangOpts());
llvm::Value *ImplicitParam =
- CGF.Builder.getInt32((IsDeleting ? 1 : 0) | (IsGlobalDelete ? 4 : 0));
+ CGF.Builder.getInt32((IsDeleting ? 1 : 0) | (IsGlobalDelete ? 4 : 0) |
+ (IsArrayDelete ? 2 : 0));
QualType ThisTy;
if (CE) {
@@ -2044,6 +2071,9 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
ThisTy = D->getDestroyedType();
}
+ while (const ArrayType *ATy = Context.getAsArrayType(ThisTy))
+ ThisTy = ATy->getElementType();
+
This = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true);
RValue RV =
CGF.EmitCXXDestructorCall(GD, Callee, This.emitRawPointer(CGF), ThisTy,
@@ -4074,6 +4104,18 @@ void MicrosoftCXXABI::emitCXXStructor(GlobalDecl GD) {
if (GD.getDtorType() == Dtor_Base && !CGM.TryEmitBaseDestructorAsAlias(dtor))
return;
+ if (GD.getDtorType() == Dtor_VectorDeleting &&
+ !CGM.classNeedsVectorDestructor(dtor->getParent())) {
+ // Create GlobalDecl object with the correct type for the scalar
+ // deleting destructor.
+ GlobalDecl ScalarDtorGD(dtor, Dtor_Deleting);
+
+ // Emit an alias from the vector deleting destructor to the scalar deleting
+ // destructor.
+ CGM.EmitDefinitionAsAlias(GD, ScalarDtorGD);
+ return;
+ }
+
llvm::Function *Fn = CGM.codegenCXXStructor(GD);
if (Fn->isWeakForLinker())
Fn->setComdat(CGM.getModule().getOrInsertComdat(Fn->getName()));
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3bc748969065a..ceae8d951796c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11139,9 +11139,11 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
else
Loc = RD->getLocation();
+ DeclarationName Name =
+ Context.DeclarationNames.getCXXOperatorName(OO_Delete);
// If we have a virtual destructor, look up the deallocation function
if (FunctionDecl *OperatorDelete = FindDeallocationFunctionForDestructor(
- Loc, RD, /*Diagnose=*/true, /*LookForGlobal=*/false)) {
+ Loc, RD, /*Diagnose=*/true, /*LookForGlobal=*/false, Name)) {
Expr *ThisArg = nullptr;
// If the notional 'delete this' expression requires a non-trivial
@@ -11189,9 +11191,33 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
// delete calls that require it.
FunctionDecl *GlobalOperatorDelete =
FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false,
- /*LookForGlobal*/ true);
+ /*LookForGlobal*/ true, Name);
Destructor->setOperatorGlobalDelete(GlobalOperatorDelete);
}
+
+ if (Context.getTargetInfo().emitVectorDeletingDtors(
+ Context.getLangOpts())) {
+ // Lookup delete[] too in case we have to emit a vector deleting dtor.
+ DeclarationName VDeleteName =
+ Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete);
+ FunctionDecl *ArrOperatorDelete = FindDeallocationFunctionForDestructor(
+ Loc, RD, /*Diagnose*/ false,
+ /*LookForGlobal*/ false, VDeleteName);
+ if (ArrOperatorDelete && isa<CXXMethodDecl>(ArrOperatorDelete)) {
+ FunctionDecl *GlobalArrOperatorDelete =
+ FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false,
+ /*LookForGlobal*/ true,
+ VDeleteName);
+ Destructor->setGlobalOperatorArrayDelete(GlobalArrOperatorDelete);
+ } else if (!ArrOperatorDelete) {
+ ArrOperatorDelete = FindDeallocationFunctionForDestructor(
+ Loc, RD, /*Diagnose*/ false,
+ /*LookForGlobal*/ true, VDeleteName);
+ }
+ assert(ArrOperatorDelete &&
+ "Should've found at least global array delete");
+ Destructor->setOperatorArrayDelete(ArrOperatorDelete);
+ }
}
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d6f70e728be29..d2388b4f9b8dc 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3612,11 +3612,9 @@ Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
return Result.FD;
}
-FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
- CXXRecordDecl *RD,
- bool Diagnose,
- bool LookForGlobal) {
- DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
+FunctionDecl *Sema::FindDeallocationFunctionForDestructor(
+ SourceLocation Loc, CXXRecordDecl *RD, bool Diagnose, bool LookForGlobal,
+ DeclarationName Name) {
FunctionDecl *OperatorDelete = nullptr;
CanQualType DeallocType = Context.getCanonicalTagType(RD);
@@ -3649,8 +3647,11 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
// Try to find operator delete/operator delete[] in class scope.
LookupQualifiedName(Found, RD);
- if (Found.isAmbiguous())
+ if (Found.isAmbiguous()) {
+ if (!Diagnose)
+ Found.suppressDiagnostics();
return true;
+ }
Found.suppressDiagnostics();
diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h
index c9b9b1bbf8743..23d3954f257e7 100644
--- a/clang/lib/Serialization/ASTCommon.h
+++ b/clang/lib/Serialization/ASTCommon.h
@@ -42,7 +42,9 @@ enum class DeclUpdateKind {
DeclMarkedOpenMPDeclareTarget,
DeclExported,
AddedAttrToRecord,
- CXXResolvedDtorGlobDelete
+ CXXResolvedDtorGlobDelete,
+ CXXResolvedDtorArrayDelete,
+ CXXResolvedDtorGlobArrayDelete
};
TypeIdx TypeIdxFromBuiltin(const BuiltinType *BT);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 5456e73956659..0ee8c3511527c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2339,19 +2339,33 @@ void ASTDeclReader::VisitCXXConstructorDecl(CXXConstructorDecl *D) {
void ASTDeclReader::VisitCXXDestructorDecl(CXXDestructorDecl *D) {
VisitCXXMethodDecl(D);
- CXXDestructorDecl *Canon = D->getCanonicalDecl();
+ ASTContext &C = Reader.getContext();
+ CXXDestructorDecl *Canon = cast<CXXDestructorDecl>(D->getCanonicalDecl());
if (auto *OperatorDelete = readDeclAs<FunctionDecl>()) {
auto *ThisArg = Record.readExpr();
// FIXME: Check consistency if we have an old and new operator delete.
- if (!Canon->OperatorDelete) {
- Canon->OperatorDelete = OperatorDelete;
+ if (!C.dtorHasOperatorDelete(D, ASTContext::OperatorDeleteKind::Regular)) {
+ C.addOperatorDeleteForVDtor(D, OperatorDelete,
+ ASTContext::OperatorDeleteKind::Regular);
Canon->OperatorDeleteThisArg = ThisArg;
}
}
if (auto *OperatorGlobDelete = readDeclAs<FunctionDecl>()) {
- if (!Canon->OperatorGlobalDelete) {
- Canon->OperatorGlobalDelete = OperatorGlobDelete;
- }
+ if (!C.dtorHasOperatorDelete(D,
+ ASTContext::OperatorDeleteKind::GlobalRegular))
+ C.addOperatorDeleteForVDtor(
+ D, OperatorGlobDelete, ASTContext::OperatorDeleteKind::GlobalRegular);
+ }
+ if (auto *OperatorArrayDelete = readDeclAs<FunctionDecl>()) {
+ if (!C.dtorHasOperatorDelete(D, ASTContext::OperatorDeleteKind::Array))
+ C.addOperatorDeleteForVDtor(D, OperatorArrayDelete,
+ ASTContext::OperatorDeleteKind::Array);
+ }
+ if (auto *OperatorGlobArrayDelete = readDeclAs<FunctionDecl>()) {
+ if (!C.dtorHasOperatorDelete(D,
+ ASTContext::OperatorDeleteKind::ArrayGlobal))
+ C.addOperatorDeleteForVDtor(D, OperatorGlobArrayDelete,
+ ASTContext::OperatorDeleteKind::ArrayGlobal);
}
}
@@ -4852,22 +4866,48 @@ void ASTDeclReader::UpdateDecl(Decl *D) {
case DeclUpdateKind::CXXResolvedDtorDelete: {
// Set the 'operator delete' directly to avoid emitting another update
// record.
+ CXXDestructorDecl *Canon = cast<CXXDestructorDecl>(D->getCanonicalDecl());
+ ASTContext &C = Reader.getContext();
auto *Del = readDeclAs<FunctionDecl>();
- auto *First = cast<CXXDestructorDecl>(D->getCanonicalDecl());
auto *ThisArg = Record.readExpr();
+ auto *Dtor = cast<CXXDestructorDecl>(D);
// FIXME: Check consistency if we have an old and new operator delete.
- if (!First->OperatorDelete) {
- First->OperatorDelete = Del;
- First->OperatorDeleteThisArg = ThisArg;
+ if (!C.dtorHasOperatorDelete(Dtor,
+ ASTContext::OperatorDeleteKind::Regular)) {
+ C.addOperatorDeleteForVDtor(Dtor, Del,
+ ASTContext::OperatorDeleteKind::Regular);
+ Canon->OperatorDeleteThisArg = ThisArg;
}
break;
}
case DeclUpdateKind::CXXResolvedDtorGlobDelete: {
auto *Del = readDeclAs<FunctionDecl>();
- auto *Canon = cast<CXXDestructorDecl>(D->getCanonicalDecl());
- if (!Canon->OperatorGlobalDelete)
- Canon->OperatorGlobalDelete = Del;
+ auto *Dtor = cast<CXXDestructorDecl>(D);
+ ASTContext &C = Reader.getContext();
+ if (!C.dtorHasOperatorDelete(
+ Dtor, ASTContext::OperatorDeleteKind::GlobalRegular))
+ C.addOperatorDeleteForVDtor(
+ Dtor, Del, ASTContext::OperatorDeleteKind::GlobalRegular);
+ break;
+ }
+ case DeclUpdateKind::CXXResolvedDtorArrayDelete: {
+ auto *Del = readDeclAs<FunctionDecl>();
+ auto *Dtor = cast<CXXDestructorDecl>(D);
+ ASTContext &C = Reader.getContext();
+ if (!C.dtorHasOperatorDelete(Dtor, ASTContext::OperatorDeleteKind::Array))
+ C.addOperatorDeleteForVDtor(Dtor, Del,
+ ASTContext::OperatorDeleteKind::Array);
+ break;
+ }
+ case DeclUpdateKind::CXXResolvedDtorGlobArrayDelete: {
+ auto *Del = readDeclAs<FunctionDecl>();
+ auto *Dtor = cast<CXXDestructorDecl>(D);
+ ASTContext &C = Reader.getContext();
+ if (!C.dtorHasOperatorDelete(Dtor,
+ ASTContext::OperatorDeleteKind::ArrayGlobal))
+ C.addOperatorDeleteForVDtor(
+ Dtor, Del, ASTContext::OperatorDeleteKind::ArrayGlobal);
break;
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index fcee93c0ebbd3..0db8358e41196 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -6531,6 +6531,14 @@ void ASTWriter::WriteDeclUpdatesBlocks(ASTContext &Context,
Record.AddDeclRef(Update.getDecl());
break;
+ case DeclUpdateKind::CXXResolvedDtorArrayDelete:
+ Record.AddDeclRef(Update.getDecl());
+ break;
+
+ case DeclUpdateKind::CXXResolvedDtorGlobArrayDelete:
+ Record.AddDeclRef(Update.getDecl());
+ break;
+
case DeclUpdateKind::CXXResolvedExceptionSpec: {
auto prototype =
cast<FunctionDecl>(D)->getType()->castAs<FunctionProtoType>();
@@ -7604,6 +7612,34 @@ void ASTWriter::ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
});
}
+void ASTWriter::ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *ArrayDelete) {
+ if (Chain && Chain->isProcessingUpdateRecords())
+ return;
+ assert(!WritingAST && "Already writing the AST!");
+ assert(ArrayDelete && "Not given an operator delete");
+ if (!Chain)
+ return;
+ Chain->forEachImportedKeyDecl(DD, [&](const Decl *D) {
+ DeclUpdates[D].push_back(
+ DeclUpdate(DeclUpdateKind::CXXResolvedDtorArrayDelete, ArrayDelete));
+ });
+}
+
+void ASTWriter::ResolvedOperatorGlobArrayDelete(
+ const CXXDestructorDecl *DD, const FunctionDecl *GlobArrayDelete) {
+ if (Chain && Chain->isProcessingUpdateRecords())
+ return;
+ assert(!WritingAST && "Already writing the AST!");
+ assert(GlobArrayDelete && "Not given an operator delete");
+ if (!Chain)
+ return;
+ Chain->forEachImportedKeyDecl(DD, [&](const Decl *D) {
+ DeclUpdates[D].push_back(DeclUpdate(
+ DeclUpdateKind::CXXResolvedDtorGlobArrayDelete, GlobArrayDelete));
+ });
+}
+
void ASTWriter::CompletedImplicitDefinition(const FunctionDecl *D) {
if (Chain && Chain->isProcessingUpdateRecords()) return;
assert(!WritingAST && "Already writing the AST!");
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index c9f8797ab973f..89e6d8e2acfec 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1794,6 +1794,8 @@ void ASTDeclWriter::VisitCXXDestructorDecl(CXXDestructorDecl *D) {
if (D->getOperatorDelete())
Record.AddStmt(D->getOperatorDeleteThisArg());
Record.AddDeclRef(D->getOperatorGlobalDelete());
+ Record.AddDeclRef(D->getArrayOperatorDelete());
+ Record.AddDeclRef(D->getGlobalArrayOperatorDelete());
Code = serialization::DECL_CXX_DESTRUCTOR;
}
diff --git a/clang/test/CodeGenCXX/dllexport.cpp b/clang/test/CodeGenCXX/dllexport.cpp
index dfbb2762ac85c..ef9d8131c511c 100644
--- a/clang/test/CodeGenCXX/dllexport.cpp
+++ b/clang/test/CodeGenCXX/dllexport.cpp
@@ -633,8 +633,9 @@ struct __declspec(dllexport) Y {
};
struct __declspec(dllexport) Z { virtual ~Z() {} };
-// The scalar deleting dtor does not get exported:
-// M32-DAG: define linkonce_odr dso_local x86_thiscallcc ptr @"??_GZ@@UAEPAXI at Z"
+// The deleting dtor does not get exported, but we emit body of vector deleting
+// destructor:
+// M32-DAG: define weak dso_local x86_thiscallcc ptr @"??_EZ@@UAEPAXI at Z"
// The user-defined dtor does get exported:
diff --git a/clang/test/CodeGenCXX/microsoft-abi-extern-template.cpp b/clang/test/CodeGenCXX/microsoft-abi-extern-template.cpp
index ea12aa64ae305..67df330bc3263 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-extern-template.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-extern-template.cpp
@@ -4,7 +4,7 @@
// own copy the vftable when emitting the available externally constructor.
// CHECK: @"??_7?$Foo at H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [
-// CHECK-SAME: ptr @"??_G?$Foo at H@@UEAAPEAXI at Z"
+// CHECK-SAME: ptr @"??_E?$Foo at H@@UEAAPEAXI at Z"
// CHECK-SAME: ] }, comdat
// CHECK-LABEL: define dso_local noundef ptr @"?f@@YAPEAU?$Foo at H@@XZ"()
diff --git a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
index 497775840e049..670988fc1ada2 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
@@ -169,7 +169,7 @@ void foo() {
// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc ptr @"??_EC at dtor_in_second_nvbase@@W3AEPAXI at Z"(ptr %this, i32 %should_call_delete)
// Do an adjustment from B* to C*.
// DTORS2: getelementptr i8, ptr %{{.*}}, i32 -4
-// DTORS2: %[[CALL:.*]] = tail call x86_thiscallcc ptr @"??_GC at dtor_in_second_nvbase@@UAEPAXI at Z"
+// DTORS2: %[[CALL:.*]] = tail call x86_thiscallcc ptr @"??_EC at dtor_in_second_nvbase@@UAEPAXI at Z"
// DTORS2: ret ptr %[[CALL]]
}
diff --git a/clang/test/CodeGenCXX/microsoft-abi-thunks.cpp b/clang/test/CodeGenCXX/microsoft-abi-thunks.cpp
index 38aa81253ccad..83ec158ff7f51 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-thunks.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-thunks.cpp
@@ -63,8 +63,7 @@ C::C() {} // Emits vftable and forces thunk generation.
// CODEGEN-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_EC@@W3AEPAXI at Z"(ptr noundef %this, i32 noundef %should_call_delete) {{.*}} comdat
// CODEGEN: getelementptr i8, ptr {{.*}}, i32 -4
-// FIXME: should actually call _EC, not _GC.
-// CODEGEN: call x86_thiscallcc noundef ptr @"??_GC@@UAEPAXI at Z"
+// CODEGEN: call x86_thiscallcc noundef ptr @"??_EC@@UAEPAXI at Z"
// CODEGEN: ret
// CODEGEN-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"?public_f at C@@W3AEXXZ"(ptr
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vftables.cpp b/clang/test/CodeGenCXX/microsoft-abi-vftables.cpp
index bc278bdb847fc..7ceb15e40e582 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vftables.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vftables.cpp
@@ -8,38 +8,38 @@ struct S {
virtual ~S();
} s;
-// RTTI-DAG: [[VTABLE_S:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4S@@6B@", ptr @"??_GS@@UAEPAXI at Z"] }, comdat($"??_7S@@6B@")
+// RTTI-DAG: [[VTABLE_S:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4S@@6B@", ptr @"??_ES@@UAEPAXI at Z"] }, comdat($"??_7S@@6B@")
// RTTI-DAG: @"??_7S@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_S]], i32 0, i32 0, i32 1)
-// NO-RTTI-DAG: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GS@@UAEPAXI at Z"] }
+// NO-RTTI-DAG: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_ES@@UAEPAXI at Z"] }
struct __declspec(dllimport) U {
virtual ~U();
} u;
-// RTTI-DAG: [[VTABLE_U:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4U@@6B@", ptr @"??_GU@@UAEPAXI at Z"] }
+// RTTI-DAG: [[VTABLE_U:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4U@@6B@", ptr @"??_EU@@UAEPAXI at Z"] }
// RTTI-DAG: @"??_SU@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_U]], i32 0, i32 0, i32 1)
-// NO-RTTI-DAG: @"??_SU@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GU@@UAEPAXI at Z"] }
+// NO-RTTI-DAG: @"??_SU@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EU@@UAEPAXI at Z"] }
struct __declspec(dllexport) V {
virtual ~V();
} v;
-// RTTI-DAG: [[VTABLE_V:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4V@@6B@", ptr @"??_GV@@UAEPAXI at Z"] }, comdat($"??_7V@@6B@")
+// RTTI-DAG: [[VTABLE_V:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4V@@6B@", ptr @"??_EV@@UAEPAXI at Z"] }, comdat($"??_7V@@6B@")
// RTTI-DAG: @"??_7V@@6B@" = dllexport unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_V]], i32 0, i32 0, i32 1)
-// NO-RTTI-DAG: @"??_7V@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GV@@UAEPAXI at Z"] }
+// NO-RTTI-DAG: @"??_7V@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EV@@UAEPAXI at Z"] }
namespace {
struct W {
virtual ~W() {}
} w;
}
-// RTTI-DAG: [[VTABLE_W:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4W@?A0x{{[^@]*}}@@6B@", ptr @"??_GW@?A0x{{[^@]*}}@@UAEPAXI at Z"] }
+// RTTI-DAG: [[VTABLE_W:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4W@?A0x{{[^@]*}}@@6B@", ptr @"??_EW@?A0x{{[^@]*}}@@UAEPAXI at Z"] }
// RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_W]], i32 0, i32 0, i32 1)
-// NO-RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GW@?A0x{{[^@]*}}@@UAEPAXI at Z"] }
+// NO-RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EW@?A0x{{[^@]*}}@@UAEPAXI at Z"] }
struct X {};
template <class> struct Y : virtual X {
@@ -49,7 +49,7 @@ template <class> struct Y : virtual X {
extern template class Y<int>;
template Y<int>::Y();
-// RTTI-DAG: [[VTABLE_Y:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4?$Y at H@@6B@", ptr @"??_G?$Y at H@@UAEPAXI at Z"] }, comdat($"??_7?$Y at H@@6B@")
+// RTTI-DAG: [[VTABLE_Y:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4?$Y at H@@6B@", ptr @"??_E?$Y at H@@UAEPAXI at Z"] }, comdat($"??_7?$Y at H@@6B@")
// RTTI-DAG: @"??_7?$Y at H@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_Y]], i32 0, i32 0, i32 1)
-// NO-RTTI-DAG: @"??_7?$Y at H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_G?$Y at H@@UAEPAXI at Z"] }, comdat
+// NO-RTTI-DAG: @"??_7?$Y at H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_E?$Y at H@@UAEPAXI at Z"] }, comdat
diff --git a/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp b/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
index b54775f6c5dd0..7e9dce18b2797 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
@@ -80,6 +80,15 @@ B::~B() {
// CHECK2: call x86_thiscallcc void @"??1VBase@@UAE at XZ"(ptr {{[^,]*}} %[[VBASE_i8]])
// CHECK2: ret
+ // CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0B at test2@@QAE at XZ"
+ // CHECK2: (ptr {{[^,]*}} returned align 4 dereferenceable(4) %this, i32 noundef %is_most_derived)
+ // CHECK2: call x86_thiscallcc noundef ptr @"??0A at test2@@QAE at XZ"(ptr {{[^,]*}} %{{.*}})
+ // CHECK2: ret
+
+ // CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GD at pr36921@@UAEPAXI at Z"(
+ // CHECK2: %[[THIS_RELOAD:.*]] = load ptr, ptr
+ // CHECK2: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -4
+
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GB@@UAEPAXI at Z"
// CHECK2: store ptr %{{.*}}, ptr %[[THIS_ADDR:.*]], align 4
// CHECK2: %[[THIS_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_PARAM_i8:.*]], i32 -8
@@ -293,11 +302,6 @@ void callC() { C x; }
// CHECK: call x86_thiscallcc noundef ptr @"??0A at test2@@QAE at XZ"(ptr {{[^,]*}} %{{.*}})
// CHECK: ret
-// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0B at test2@@QAE at XZ"
-// CHECK2: (ptr {{[^,]*}} returned align 4 dereferenceable(4) %this, i32 noundef %is_most_derived)
-// CHECK2: call x86_thiscallcc noundef ptr @"??0A at test2@@QAE at XZ"(ptr {{[^,]*}} %{{.*}})
-// CHECK2: ret
-
}
namespace test3 {
@@ -480,9 +484,6 @@ struct B {
struct C : virtual B {};
struct D : virtual A, C {};
D d;
-// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GD at pr36921@@UAEPAXI at Z"(
-// CHECK2: %[[THIS_RELOAD:.*]] = load ptr, ptr
-// CHECK2: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -4
}
namespace issue_60465 {
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance-vdtors.cpp b/clang/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance-vdtors.cpp
index a407766f8ed9f..74150b0ecb535 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance-vdtors.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance-vdtors.cpp
@@ -12,18 +12,18 @@ struct B {
struct C : A, B {
// CHECK-LABEL: VFTable for 'A' in 'C' (2 entries).
- // CHECK-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-NEXT: 0 | C::~C() [vector deleting]
// CHECK-NEXT: 1 | void A::z1()
// CHECK-LABEL: VFTable for 'B' in 'C' (1 entry).
- // CHECK-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-NEXT: 0 | C::~C() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-LABEL: Thunks for 'C::~C()' (1 entry).
// CHECK-NEXT: 0 | [this adjustment: -4 non-virtual]
// CHECK-LABEL: VFTable indices for 'C' (1 entry).
- // CHECK-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-NEXT: 0 | C::~C() [vector deleting]
virtual ~C();
};
@@ -41,7 +41,7 @@ struct E : D, B {
// CHECK-NEXT: 0 | void D::z4()
// CHECK-LABEL: VFTable for 'B' in 'E' (1 entry).
- // CHECK-NEXT: 0 | E::~E() [scalar deleting]
+ // CHECK-NEXT: 0 | E::~E() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-LABEL: Thunks for 'E::~E()' (1 entry).
@@ -49,7 +49,7 @@ struct E : D, B {
// CHECK-LABEL: VFTable indices for 'E' (1 entry).
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
- // CHECK-NEXT: 0 | E::~E() [scalar deleting]
+ // CHECK-NEXT: 0 | E::~E() [vector deleting]
};
void build_vftable(E *obj) { delete obj; }
@@ -61,7 +61,7 @@ struct F : D, B {
// CHECK-NEXT: 0 | void D::z4()
// CHECK-LABEL: VFTable for 'B' in 'F' (1 entry).
- // CHECK-NEXT: 0 | F::~F() [scalar deleting]
+ // CHECK-NEXT: 0 | F::~F() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-LABEL: Thunks for 'F::~F()' (1 entry).
@@ -69,7 +69,7 @@ struct F : D, B {
// CHECK-LABEL: VFTable indices for 'F' (1 entry).
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
- // CHECK-NEXT: 0 | F::~F() [scalar deleting]
+ // CHECK-NEXT: 0 | F::~F() [vector deleting]
};
void build_vftable(F *obj) { delete obj; }
@@ -79,7 +79,7 @@ struct G : F {
// CHECK-NEXT: 0 | void D::z4()
// CHECK-LABEL: VFTable for 'B' in 'F' in 'G' (1 entry).
- // CHECK-NEXT: 0 | G::~G() [scalar deleting]
+ // CHECK-NEXT: 0 | G::~G() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-LABEL: Thunks for 'G::~G()' (1 entry).
@@ -87,7 +87,7 @@ struct G : F {
// CHECK-LABEL: VFTable indices for 'G' (1 entry).
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
- // CHECK-NEXT: 0 | G::~G() [scalar deleting]
+ // CHECK-NEXT: 0 | G::~G() [vector deleting]
virtual ~G();
};
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp b/clang/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
index 5030a5dcd2a50..1a589370d3a74 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
@@ -213,6 +213,6 @@ struct C : virtual B { C *f(); };
C c;
// VFTABLES-LABEL: VFTable indices for 'pr34302::C' (2 entries).
// VFTABLES-NEXT: -- accessible via vbtable index 1, vfptr at offset 0 --
-// VFTABLES-NEXT: 0 | pr34302::C::~C() [scalar deleting]
+// VFTABLES-NEXT: 0 | pr34302::C::~C() [vector deleting]
// VFTABLES-NEXT: 2 | C *pr34302::C::f()
}
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp b/clang/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
index b0bf927d38f7c..c95202e8cc253 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
@@ -44,10 +44,10 @@ void use(B *obj) { obj->f(); }
struct C {
// CHECK-LABEL: VFTable for 'C' (2 entries)
- // CHECK-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-NEXT: 0 | C::~C() [vector deleting]
// CHECK-NEXT: 1 | void C::f()
// CHECK-LABEL: VFTable indices for 'C' (2 entries).
- // CHECK-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-NEXT: 0 | C::~C() [vector deleting]
// CHECK-NEXT: 1 | void C::f()
virtual ~C();
@@ -60,10 +60,10 @@ void use(C *obj) { obj->f(); }
struct D {
// CHECK-LABEL: VFTable for 'D' (2 entries)
// CHECK-NEXT: 0 | void D::f()
- // CHECK-NEXT: 1 | D::~D() [scalar deleting]
+ // CHECK-NEXT: 1 | D::~D() [vector deleting]
// CHECK-LABEL: VFTable indices for 'D' (2 entries)
// CHECK-NEXT: 0 | void D::f()
- // CHECK-NEXT: 1 | D::~D() [scalar deleting]
+ // CHECK-NEXT: 1 | D::~D() [vector deleting]
virtual void f();
virtual ~D();
@@ -77,10 +77,10 @@ struct E : A {
// CHECK-NEXT: 0 | void A::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
- // CHECK-NEXT: 3 | E::~E() [scalar deleting]
+ // CHECK-NEXT: 3 | E::~E() [vector deleting]
// CHECK-NEXT: 4 | void E::i()
// CHECK-LABEL: VFTable indices for 'E' (2 entries).
- // CHECK-NEXT: 3 | E::~E() [scalar deleting]
+ // CHECK-NEXT: 3 | E::~E() [vector deleting]
// CHECK-NEXT: 4 | void E::i()
// ~E would be the key method, but it isn't used, and MS ABI has no key
@@ -98,10 +98,10 @@ struct F : A {
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-NEXT: 3 | void F::i()
- // CHECK-NEXT: 4 | F::~F() [scalar deleting]
+ // CHECK-NEXT: 4 | F::~F() [vector deleting]
// CHECK-LABEL: VFTable indices for 'F' (2 entries).
// CHECK-NEXT: 3 | void F::i()
- // CHECK-NEXT: 4 | F::~F() [scalar deleting]
+ // CHECK-NEXT: 4 | F::~F() [vector deleting]
virtual void i();
virtual ~F();
@@ -115,12 +115,12 @@ struct G : E {
// CHECK-NEXT: 0 | void G::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
- // CHECK-NEXT: 3 | G::~G() [scalar deleting]
+ // CHECK-NEXT: 3 | G::~G() [vector deleting]
// CHECK-NEXT: 4 | void E::i()
// CHECK-NEXT: 5 | void G::j()
// CHECK-LABEL: VFTable indices for 'G' (3 entries).
// CHECK-NEXT: 0 | void G::f()
- // CHECK-NEXT: 3 | G::~G() [scalar deleting]
+ // CHECK-NEXT: 3 | G::~G() [vector deleting]
// CHECK-NEXT: 5 | void G::j()
virtual void f(); // overrides A::f()
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp b/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
index c5ce69f5cbcac..be9f281560dcf 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
@@ -57,7 +57,7 @@ struct A : virtual V1 {
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries).
// CHECK-NEXT: 0 | void simple::A::f()
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
- // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::A::~A() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
@@ -79,7 +79,7 @@ void use(A *obj) { obj->f(); }
struct B : virtual V3 {
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
// CHECK-NEXT: 0 | void Z::g()
- // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::B::~B() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
@@ -88,7 +88,7 @@ struct B : virtual V3 {
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
// CHECK-NEXT: 0 | void simple::B::f()
// CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
- // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::B::~B() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
// CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
@@ -115,7 +115,7 @@ void use(B *obj) { obj->f(); }
struct C : virtual V4 {
// CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
// CHECK-NEXT: 0 | void Z::g()
- // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
@@ -124,7 +124,7 @@ struct C : virtual V4 {
// CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
// CHECK-NEXT: 0 | void simple::C::f()
// CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
- // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
@@ -136,7 +136,7 @@ struct C : virtual V4 {
// CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
// CHECK-NEXT: 0 | void simple::C::f()
// CHECK-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
- // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
@@ -162,7 +162,7 @@ class D : B {
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries).
// CHECK-NEXT: 0 | void simple::B::f()
// CHECK-NEXT: [this adjustment: vtordisp at -12, -4 non-virtual]
- // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::D::~D() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
D();
int z;
@@ -180,12 +180,12 @@ struct F : virtual E {
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
// CHECK-NEXT: 0 | void simple::F::g()
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
- // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::F::~F() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
// CHECK-NEXT: 0 | void simple::E::f()
- // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::F::~F() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
F();
@@ -202,12 +202,12 @@ struct G : F {
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
// CHECK-NEXT: 0 | void simple::F::g()
// CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
- // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::G::~G() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
// CHECK-NEXT: 0 | void simple::E::f()
- // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
+ // CHECK-NEXT: 1 | simple::G::~G() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
G();
@@ -240,7 +240,7 @@ struct A : virtual simple::A {
// CHECK-NEXT: 0 | void simple::A::f()
// CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
// CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
- // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting]
+ // CHECK-NEXT: 1 | extended::A::~A() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
@@ -265,7 +265,7 @@ struct B : virtual simple::A {
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
// ...
- // CHECK: 1 | extended::B::~B() [scalar deleting]
+ // CHECK: 1 | extended::B::~B() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
@@ -353,7 +353,7 @@ struct G : virtual simple::A {
// CHECK-NEXT: 0 | void simple::A::f()
// CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
// CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
- // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting]
+ // CHECK-NEXT: 1 | extended::G::~G() [vector deleting]
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
@@ -374,7 +374,7 @@ void use(G *obj) { obj->g(); }
struct H : Z, A {
// CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries).
// CHECK-NEXT: 0 | void Z::g()
- // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting]
+ // CHECK-NEXT: 1 | extended::H::~H() [vector deleting]
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
// CHECK-NEXT: 0 | void simple::A::f()
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp b/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
index 257ba270291c8..e5e6ea5f42c1c 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
@@ -492,7 +492,7 @@ struct X {
struct Y : virtual X {
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries).
- // CHECK-NEXT: 0 | vdtors::Y::~Y() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::Y::~Y() [vector deleting]
// CHECK-NEXT: 1 | void vdtors::X::zzz()
// CHECK-NOT: Thunks for 'vdtors::Y::~Y()'
@@ -515,7 +515,7 @@ struct U : virtual W {
// CHECK-NEXT: 0 | void vdtors::Z::z()
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries).
- // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::U::~U() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-NEXT: 1 | void vdtors::X::zzz()
@@ -524,7 +524,7 @@ struct U : virtual W {
// CHECK-LABEL: VFTable indices for 'vdtors::U' (1 entry).
// CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
- // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::U::~U() [vector deleting]
virtual ~U();
};
@@ -536,7 +536,7 @@ struct V : virtual W {
// CHECK-NEXT: 0 | void vdtors::Z::z()
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries).
- // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::V::~V() [vector deleting]
// CHECK-NEXT: [this adjustment: -4 non-virtual]
// CHECK-NEXT: 1 | void vdtors::X::zzz()
@@ -545,7 +545,7 @@ struct V : virtual W {
// CHECK-LABEL: VFTable indices for 'vdtors::V' (1 entry).
// CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
- // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::V::~V() [vector deleting]
};
V v;
@@ -557,7 +557,7 @@ struct T : virtual X {
struct P : T, Y {
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries).
- // CHECK-NEXT: 0 | vdtors::P::~P() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::P::~P() [vector deleting]
// CHECK-NEXT: 1 | void vdtors::X::zzz()
// CHECK-NOT: Thunks for 'vdtors::P::~P()'
@@ -574,18 +574,18 @@ struct Q {
// PR19172: Yet another diamond we miscompiled.
struct R : virtual Q, X {
// CHECK-LABEL: VFTable for 'vdtors::Q' in 'vdtors::R' (1 entry).
- // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
// CHECK-NEXT: [this adjustment: -8 non-virtual]
// CHECK-LABEL: Thunks for 'vdtors::R::~R()' (1 entry).
// CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::R' (2 entries).
- // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
// CHECK-NEXT: 1 | void vdtors::X::zzz()
// CHECK-LABEL: VFTable indices for 'vdtors::R' (1 entry).
- // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
+ // CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
virtual ~R();
};
diff --git a/clang/test/CodeGenCXX/microsoft-no-rtti-data.cpp b/clang/test/CodeGenCXX/microsoft-no-rtti-data.cpp
index 069f0226ab948..c8e374e51a031 100644
--- a/clang/test/CodeGenCXX/microsoft-no-rtti-data.cpp
+++ b/clang/test/CodeGenCXX/microsoft-no-rtti-data.cpp
@@ -2,7 +2,7 @@
// vftable shouldn't have RTTI data in it.
// CHECK-NOT: @"??_R4S@@6B@"
-// CHECK: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GS@@UAEPAXI at Z"] }, comdat
+// CHECK: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_ES@@UAEPAXI at Z"] }, comdat
struct type_info;
namespace std { using ::type_info; }
diff --git a/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp
new file mode 100644
index 0000000000000..e8012abb79aee
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp
@@ -0,0 +1,336 @@
+// RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=x86_64-pc-windows-msvc -o - | FileCheck --check-prefixes=X64,CHECK %s
+// RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=i386-pc-windows-msvc -o - | FileCheck --check-prefixes=X86,CHECK %s
+// RUN: %clang_cc1 -emit-llvm -fms-extensions %s -triple=x86_64-pc-windows-msvc -fclang-abi-compat=21 -o - | FileCheck --check-prefixes=CLANG21 %s
+
+struct Bird {
+ virtual ~Bird();
+};
+
+struct Parrot : public Bird {
+// X64: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_EParrot@@UEAAPEAXI at Z"] }, comdat($"??_7Parrot@@6B@")
+// X86: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_EParrot@@UAEPAXI at Z"] }, comdat($"??_7Parrot@@6B@")
+// CLANG21: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_GParrot@@UEAAPEAXI at Z"] }, comdat($"??_7Parrot@@6B@")
+// X64: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_EBird@@UEAAPEAXI at Z"] }, comdat($"??_7Bird@@6B@")
+// X86: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_EBird@@UAEPAXI at Z"] }, comdat($"??_7Bird@@6B@")
+// CLANG21: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_GBird@@UEAAPEAXI at Z"] }, comdat($"??_7Bird@@6B@")
+ virtual ~Parrot() {}
+};
+
+Bird::~Bird() {}
+
+// For the weird bird we first emit scalar deleting destructor, then find out
+// that we need vector deleting destructor and remove the alias.
+struct JustAWeirdBird {
+ virtual ~JustAWeirdBird() {}
+
+ bool doSmth(int n) {
+ JustAWeirdBird *c = new JustAWeirdBird[n];
+
+ delete[] c;
+ return true;
+ }
+};
+
+int i = 0;
+struct HasOperatorDelete : public Bird{
+~HasOperatorDelete() { }
+void operator delete(void *p) { i-=2; }
+void operator delete[](void *p) { i--; }
+};
+
+struct AllocatedAsArray : public Bird {
+
+};
+
+// Vector deleting dtor for Bird is an alias because no new Bird[] expressions
+// in the TU.
+// X64: @"??_EBird@@UEAAPEAXI at Z" = weak dso_local unnamed_addr alias ptr (ptr, i32), ptr @"??_GBird@@UEAAPEAXI at Z"
+// X86: @"??_EBird@@UAEPAXI at Z" = weak dso_local unnamed_addr alias ptr (ptr, i32), ptr @"??_GBird@@UAEPAXI at Z"
+// No scalar destructor for Parrot.
+// CHECK-NOT: @"??_GParrot"
+// No vector destructor definition for Bird.
+// CHECK-NOT: define{{.*}}@"??_EBird"
+// No scalar deleting dtor for JustAWeirdBird.
+// CHECK-NOT: @"??_GJustAWeirdBird"
+// CLANG21-NOT: @"??_E
+
+void dealloc(Bird *p) {
+ delete[] p;
+}
+
+Bird* alloc() {
+ Parrot* P = new Parrot[38];
+ return P;
+}
+
+
+template<class C>
+struct S {
+ void foo() { void *p = new C(); delete (C *)p; }
+};
+
+S<AllocatedAsArray[1][3]> sp;
+
+void bar() {
+ dealloc(alloc());
+
+ JustAWeirdBird B;
+ B.doSmth(38);
+
+ Bird *p = new HasOperatorDelete[2];
+ dealloc(p);
+
+ sp.foo();
+}
+
+// CHECK-LABEL: define dso_local void @{{.*}}dealloc{{.*}}(
+// CHECK-SAME: ptr noundef %[[PTR:.*]])
+// CHECK: entry:
+// CHECK-NEXT: %[[PTRADDR:.*]] = alloca ptr
+// CHECK-NEXT: store ptr %[[PTR]], ptr %[[PTRADDR]]
+// CHECK-NEXT: %[[LPTR:.*]] = load ptr, ptr %[[PTRADDR]]
+// CHECK-NEXT: %[[ISNULL:.*]] = icmp eq ptr %[[LPTR]], null
+// CHECK-NEXT: br i1 %[[ISNULL]], label %delete.end, label %delete.notnull
+// CHECK: delete.notnull:
+// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LPTR]], i64 -8
+// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LPTR]], i32 -4
+// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
+// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
+// X64-NEXT: %[[ISNOELEM:.*]] = icmp eq i64 %2, 0
+// X86-NEXT: %[[ISNOELEM:.*]] = icmp eq i32 %2, 0
+// CHECK-NEXT: br i1 %[[ISNOELEM]], label %vdtor.nocall, label %vdtor.call
+// CHECK: vdtor.nocall:
+// X64-NEXT: %[[HOWMANYBYTES:.*]] = mul i64 8, %[[HOWMANY]]
+// X86-NEXT: %[[HOWMANYBYTES:.*]] = mul i32 4, %[[HOWMANY]]
+// X64-NEXT: %[[ADDCOOKIESIZE:.*]] = add i64 %[[HOWMANYBYTES]], 8
+// X86-NEXT: %[[ADDCOOKIESIZE:.*]] = add i32 %[[HOWMANYBYTES]], 4
+// X64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef %[[ADDCOOKIESIZE]])
+// X86-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef %[[ADDCOOKIESIZE]])
+// CHECK-NEXT: br label %delete.end
+// CHECK: vdtor.call:
+// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[LPTR]]
+// CHECK-NEXT: %[[FPGEP:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0
+// CHECK-NEXT: %[[FPLOAD:.*]] = load ptr, ptr %[[FPGEP]]
+// X64-NEXT: %[[CALL:.*]] = call noundef ptr %[[FPLOAD]](ptr noundef nonnull align 8 dereferenceable(8) %[[LPTR]], i32 noundef 3)
+// X86-NEXT: %[[CALL:.*]] = call x86_thiscallcc noundef ptr %[[FPLOAD]](ptr noundef nonnull align 4 dereferenceable(4) %[[LPTR]], i32 noundef 3)
+// CHECK-NEXT: br label %delete.end
+// CHECK: delete.end:
+// CHECK-NEXT: ret void
+
+// Normal loop over the array elements for clang21 ABI
+// CLANG21-LABEL: define dso_local void @"?dealloc@@YAXPEAUBird@@@Z"
+// CLANG21: %p.addr = alloca ptr
+// CLANG21-NEXT: store ptr %p, ptr %p.addr
+// CLANG21-NEXT: %0 = load ptr, ptr %p.addr
+// CLANG21-NEXT: %isnull = icmp eq ptr %0, null
+// CLANG21-NEXT: br i1 %isnull, label %delete.end2, label %delete.notnull
+// CLANG21: delete.notnull:
+// CLANG21-NEXT: %1 = getelementptr inbounds i8, ptr %0, i64 -8
+// CLANG21-NEXT: %2 = load i64, ptr %1
+// CLANG21-NEXT: %delete.end = getelementptr inbounds %struct.Bird, ptr %0, i64 %2
+// CLANG21-NEXT: %arraydestroy.isempty = icmp eq ptr %0, %delete.end
+// CLANG21-NEXT: br i1 %arraydestroy.isempty, label %arraydestroy.done1, label %arraydestroy.body
+// CLANG21: arraydestroy.body:
+// CLANG21-NEXT: %arraydestroy.elementPast = phi ptr [ %delete.end, %delete.notnull ], [ %arraydestroy.element, %arraydestroy.body ]
+// CLANG21-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Bird, ptr %arraydestroy.elementPast, i64 -1
+// CLANG21-NEXT: call void @"??1Bird@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)
+// CLANG21-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0
+// CLANG21-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done1, label %arraydestroy.body
+// CLANG21: arraydestroy.done1:
+// CLANG21-NEXT: %3 = mul i64 8, %2
+// CLANG21-NEXT: %4 = add i64 %3, 8
+// CLANG21-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %1, i64 noundef %4)
+// CLANG21-NEXT: br label %delete.end2
+
+// Definition of S::foo, check that it has vector deleting destructor call
+// X64-LABEL: define linkonce_odr dso_local void @"?foo@?$S@$$BY102UAllocatedAsArray@@@@QEAAXXZ"
+// X86-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"?foo@?$S@$$BY102UAllocatedAsArray@@@@QAEXXZ"
+// X64: %[[NEWCALL:.*]] = call noalias noundef nonnull ptr @"??_U at YAPEAX_K@Z"(i64 noundef 32)
+// X86: %[[NEWCALL:.*]] = call noalias noundef nonnull ptr @"??_U at YAPAXI@Z"(i32 noundef 16)
+// X64: %[[ARR:.*]] = getelementptr inbounds i8, ptr %[[NEWCALL]], i64 8
+// X86: %[[ARR:.*]] = getelementptr inbounds i8, ptr %[[NEWCALL]], i32 4
+// CHECK: store ptr %[[ARR]], ptr %[[DP:.*]]
+// CHECK: %[[DEL_PTR:.*]] = load ptr, ptr %[[DP:.*]]
+// CHECK: delete.notnull:
+// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[DEL_PTR]], i64 -8
+// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[DEL_PTR]], i32 -4
+// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
+// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
+// X64-NEXT: %[[ISNOELEM:.*]] = icmp eq i64 %[[HOWMANY]], 0
+// X86-NEXT: %[[ISNOELEM:.*]] = icmp eq i32 %[[HOWMANY]], 0
+// CHECK-NEXT: br i1 %[[ISNOELEM]], label %vdtor.nocall, label %vdtor.call
+// CHECK: vdtor.nocall: ; preds = %delete.notnull
+// X64-NEXT: %[[HOWMANYBYTES:.*]] = mul i64 8, %[[HOWMANY]]
+// X86-NEXT: %[[HOWMANYBYTES:.*]] = mul i32 4, %[[HOWMANY]]
+// X64-NEXT: %[[ADDCOOKIESIZE:.*]] = add i64 %[[HOWMANYBYTES]], 8
+// X86-NEXT: %[[ADDCOOKIESIZE:.*]] = add i32 %[[HOWMANYBYTES]], 4
+// X64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef %[[ADDCOOKIESIZE]])
+// X86-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef %[[ADDCOOKIESIZE]])
+// CHECK-NEXT: br label %delete.end
+// CHECK: vdtor.call: ; preds = %delete.notnull
+// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[DEL_PTR]]
+// CHECK-NEXT: %[[FPGEP:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0
+// CHECK-NEXT: %[[FPLOAD:.*]] = load ptr, ptr %[[FPGEP]]
+// X64-NEXT: %[[CALL:.*]] = call noundef ptr %[[FPLOAD]](ptr noundef nonnull align 8 dereferenceable(8) %[[DEL_PTR]], i32 noundef 3)
+// X86-NEXT: %[[CALL:.*]] = call x86_thiscallcc noundef ptr %[[FPLOAD]](ptr noundef nonnull align 4 dereferenceable(4) %[[DEL_PTR]], i32 noundef 3)
+// CHECK-NEXT: br label %delete.end
+// CHECK: delete.end:
+// CHECK-NEXT: ret void
+
+// Vector dtor definition for Parrot.
+// X64-LABEL: define weak dso_local noundef ptr @"??_EParrot@@UEAAPEAXI at Z"(
+// X64-SAME: ptr {{.*}} %[[THIS:.*]], i32 {{.*}} %[[IMPLICIT_PARAM:.*]]) unnamed_addr
+// X86-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EParrot@@UAEPAXI at Z"(
+// X86-SAME: ptr noundef nonnull align 4 dereferenceable(4) %[[THIS:.*]], i32 noundef %[[IMPLICIT_PARAM:.*]]) unnamed_addr
+// CHECK: entry:
+// CHECK-NEXT: %[[RET:.*]] = alloca ptr
+// CHECK-NEXT: %[[IPADDR:.*]] = alloca i32
+// CHECK-NEXT: %[[THISADDR:.*]] = alloca ptr
+// CHECK-NEXT: store i32 %[[IMPLICIT_PARAM]], ptr %[[IPADDR]]
+// CHECK-NEXT: store ptr %[[THIS]], ptr %[[THISADDR]]
+// CHECK-NEXT: %[[LTHIS:.*]] = load ptr, ptr %[[THISADDR]]
+// CHECK-NEXT: store ptr %[[LTHIS]], ptr %[[RET]]
+// CHECK-NEXT: %[[LIP:.*]] = load i32, ptr %[[IPADDR]]
+// CHECK-NEXT: %[[SECONDBIT:.*]] = and i32 %[[LIP]], 2
+// CHECK-NEXT: %[[ISSECONDBITZERO:.*]] = icmp eq i32 %[[SECONDBIT]], 0
+// CHECK-NEXT: br i1 %[[ISSECONDBITZERO:.*]], label %dtor.scalar, label %dtor.vector
+// CHECK: dtor.vector:
+// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LTHIS]], i64 -8
+// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LTHIS]], i32 -4
+// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
+// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
+// X64-NEXT: %[[END:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[LTHIS]], i64 %[[HOWMANY]]
+// X86-NEXT: %[[END:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[LTHIS]], i32 %[[HOWMANY]]
+// CHECK-NEXT: br label %arraydestroy.body
+// CHECK: arraydestroy.body:
+// CHECK-NEXT: %[[PASTELEM:.*]] = phi ptr [ %delete.end, %dtor.vector ], [ %arraydestroy.element, %arraydestroy.body ]
+// X64-NEXT: %[[CURELEM:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[PASTELEM]], i64 -1
+// X86-NEXT: %[[CURELEM:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[PASTELEM]], i32 -1
+// X64-NEXT: call void @"??1Parrot@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[CURELEM]])
+// X86-NEXT: call x86_thiscallcc void @"??1Parrot@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %[[CURELEM]])
+// CHECK-NEXT: %[[DONE:.*]] = icmp eq ptr %[[CURELEM]], %[[LTHIS]]
+// CHECK-NEXT: br i1 %[[DONE]], label %arraydestroy.done3, label %arraydestroy.body
+// CHECK: arraydestroy.done3:
+// CHECK-NEXT: br label %dtor.vector.cont
+// CHECK: dtor.vector.cont:
+// CHECK-NEXT: %[[FIRSTBIT:.*]] = and i32 %[[LIP]], 1
+// CHECK-NEXT: %[[ISFIRSTBITZERO:.*]] = icmp eq i32 %[[FIRSTBIT]], 0
+// CHECK-NEXT: br i1 %[[ISFIRSTBITZERO]], label %dtor.continue, label %dtor.call_delete_after_array_destroy
+// CHECK: dtor.call_delete_after_array_destroy:
+// X64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef 8)
+// X86-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef 4)
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.scalar:
+// X64-NEXT: call void @"??1Parrot@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[LTHIS]])
+// X86-NEXT: call x86_thiscallcc void @"??1Parrot@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %[[LTHIS]])
+// CHECK-NEXT: %[[FIRSTBIT:.*]] = and i32 %[[LIP]], 1
+// CHECK-NEXT: %[[ISFIRSTBITZERO:.*]] = icmp eq i32 %[[FIRSTBIT]], 0
+// CHECK-NEXT: br i1 %[[ISFIRSTBITZERO]], label %dtor.continue, label %dtor.call_delete
+// CHECK: dtor.call_delete:
+// X64-NEXT: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %[[LTHIS]], i64 noundef 8)
+// X86-NEXT: call void @"??3 at YAXPAXI@Z"(ptr noundef %[[LTHIS]], i32 noundef 4)
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.continue:
+// CHECK-NEXT: %[[LOADRET:.*]] = load ptr, ptr %[[RET]]
+// CHECK-NEXT: ret ptr %[[LOADRET]]
+
+// X64: define weak dso_local noundef ptr @"??_EJustAWeirdBird@@UEAAPEAXI at Z"(
+// X64-SAME: ptr noundef nonnull align 8 dereferenceable(8) %this, i32 noundef %should_call_delete)
+// CLANG21: define linkonce_odr dso_local noundef ptr @"??_GJustAWeirdBird@@UEAAPEAXI at Z"(
+// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_EJustAWeirdBird@@UAEPAXI at Z"(
+// X86-SAME: ptr noundef nonnull align 4 dereferenceable(4) %this, i32 noundef %should_call_delete) unnamed_addr
+
+// X64-LABEL: define weak dso_local noundef ptr @"??_EHasOperatorDelete@@UEAAPEAXI at Z"
+// X86-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EHasOperatorDelete@@UAEPAXI at Z"
+// CLANG21: define linkonce_odr dso_local noundef ptr @"??_GHasOperatorDelete@@UEAAPEAXI at Z"
+// CHECK: dtor.call_delete_after_array_destroy:
+// CHECK-NEXT: %[[SHOULD_CALL_GLOB_DELETE:.*]] = and i32 %should_call_delete2, 4
+// CHECK-NEXT: %[[CHK:.*]] = icmp eq i32 %[[SHOULD_CALL_GLOB_DELETE]], 0
+// CHECK-NEXT: br i1 %[[CHK]], label %dtor.call_class_delete_after_array_destroy, label %dtor.call_glob_delete_after_array_destroy
+// CHECK: dtor.call_class_delete_after_array_destroy:
+// X64-NEXT: call void @"??_VHasOperatorDelete@@SAXPEAX at Z"(ptr noundef %2)
+// X86-NEXT: call void @"??_VHasOperatorDelete@@SAXPAX at Z"
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.call_glob_delete_after_array_destroy:
+// X64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 8)
+// X86-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %2, i32 noundef 4)
+// CHECK-NEXT: br label %dtor.continue
+
+
+
+struct BaseDelete1 {
+ void operator delete[](void *);
+};
+struct BaseDelete2 {
+ void operator delete[](void *);
+};
+struct BaseDestructor {
+ BaseDestructor() {}
+ virtual ~BaseDestructor() = default;
+};
+
+struct Derived : BaseDelete1, BaseDelete2, BaseDestructor {
+ Derived() {}
+};
+
+void foobartest() {
+ Derived *a = new Derived[10]();
+ ::delete[] a;
+}
+
+// X64-LABEL: define weak dso_local noundef ptr @"??_EDerived@@UEAAPEAXI at Z"(ptr {{.*}} %this, i32 noundef %should_call_delete)
+// X86-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EDerived@@UAEPAXI at Z"(ptr {{.*}} %this, i32 noundef %should_call_delete)
+// CHECK: %retval = alloca ptr
+// CHECK-NEXT: %should_call_delete.addr = alloca i32, align 4
+// CHECK-NEXT: %this.addr = alloca ptr
+// CHECK-NEXT: store i32 %should_call_delete, ptr %should_call_delete.addr, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr
+// CHECK-NEXT: store ptr %this1, ptr %retval
+// CHECK-NEXT: %should_call_delete2 = load i32, ptr %should_call_delete.addr, align 4
+// CHECK-NEXT: %0 = and i32 %should_call_delete2, 2
+// CHECK-NEXT: %1 = icmp eq i32 %0, 0
+// CHECK-NEXT: br i1 %1, label %dtor.scalar, label %dtor.vector
+// CHECK: dtor.vector:
+// X64-NEXT: %2 = getelementptr inbounds i8, ptr %this1, i64 -8
+// X86-NEXT: %2 = getelementptr inbounds i8, ptr %this1, i32 -4
+// X64-NEXT: %3 = load i64, ptr %2
+// X86-NEXT: %3 = load i32, ptr %2
+// X64-NEXT: %delete.end = getelementptr inbounds %struct.Derived, ptr %this1, i64 %3
+// X86-NEXT: %delete.end = getelementptr inbounds %struct.Derived, ptr %this1, i32 %3
+// CHECK-NEXT: br label %arraydestroy.body
+// CHECK: arraydestroy.body:
+// CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %delete.end, %dtor.vector ], [ %arraydestroy.element, %arraydestroy.body ]
+// X64-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Derived, ptr %arraydestroy.elementPast, i64 -1
+// X86-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Derived, ptr %arraydestroy.elementPast, i32 -1
+// X64-NEXT: call void @"??1Derived@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(16) %arraydestroy.element)
+// X86-NEXT: call x86_thiscallcc void @"??1Derived@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(8) %arraydestroy.element)
+// CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %this1
+// CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done3, label %arraydestroy.body
+// CHECK: arraydestroy.done3:
+// CHECK-NEXT: br label %dtor.vector.cont
+// CHECK: dtor.vector.cont:
+// CHECK-NEXT: %4 = and i32 %should_call_delete2, 1
+// CHECK-NEXT: %5 = icmp eq i32 %4, 0
+// CHECK-NEXT: br i1 %5, label %dtor.continue, label %dtor.call_delete_after_array_destroy
+// CHECK: dtor.call_delete_after_array_destroy:
+// X64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 16)
+// X86-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %2, i32 noundef 8)
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.scalar:
+// X64-NEXT: call void @"??1Derived@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(16) %this1)
+// X86-NEXT: call x86_thiscallcc void @"??1Derived@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(8) %this1)
+// CHECK-NEXT: %6 = and i32 %should_call_delete2, 1
+// CHECK-NEXT: %7 = icmp eq i32 %6, 0
+// CHECK-NEXT: br i1 %7, label %dtor.continue, label %dtor.call_delete
+// CHECK: dtor.call_delete:
+// X64-NEXT: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %this1, i64 noundef 16)
+// X86-NEXT: call void @"??3 at YAXPAXI@Z"(ptr noundef %this1, i32 noundef 8)
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.continue:
+// CHECK-NEXT: %8 = load ptr, ptr %retval
+// CHECK-NEXT: ret ptr %8
+
+// X64: define weak dso_local noundef ptr @"??_EAllocatedAsArray@@UEAAPEAXI at Z"
+// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_EAllocatedAsArray@@UAEPAXI at Z"
+// CLANG21: define linkonce_odr dso_local noundef ptr @"??_GAllocatedAsArray@@UEAAPEAXI at Z"
diff --git a/clang/test/CodeGenCXX/vtable-consteval.cpp b/clang/test/CodeGenCXX/vtable-consteval.cpp
index 1454f6fde357d..220143465c574 100644
--- a/clang/test/CodeGenCXX/vtable-consteval.cpp
+++ b/clang/test/CodeGenCXX/vtable-consteval.cpp
@@ -26,7 +26,7 @@ struct B {
B b;
// ITANIUM-DAG: @_ZTV1C = {{.*}} constant { [4 x ptr] } {{.*}} null, ptr @_ZTI1C, ptr @_ZN1CD1Ev, ptr @_ZN1CD0Ev
-// MSABI-DAG: @[[C_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4C@@6B@", ptr @"??_GC@@UEAAPEAXI at Z"
+// MSABI-DAG: @[[C_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4C@@6B@", ptr @"??_EC@@UEAAPEAXI at Z"
struct C {
virtual ~C() = default;
virtual consteval C &operator=(const C&) = default;
@@ -36,7 +36,7 @@ struct C {
C c;
// ITANIUM-DAG: @_ZTV1D = {{.*}} constant { [4 x ptr] } {{.*}} null, ptr @_ZTI1D, ptr @_ZN1DD1Ev, ptr @_ZN1DD0Ev
-// MSABI-DAG: @[[D_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4D@@6B@", ptr @"??_GD@@UEAAPEAXI at Z"
+// MSABI-DAG: @[[D_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4D@@6B@", ptr @"??_ED@@UEAAPEAXI at Z"
struct D : C {};
// ITANIUM-DAG: @d = {{.*}}global { ptr } { {{.*}} @_ZTV1D,
// MSABI-DAG: @"?d@@3UD@@A" = {{.*}}global { ptr } { ptr @"??_7D@@6B@" }
diff --git a/clang/test/DebugInfo/CXX/windows-dtor.cpp b/clang/test/DebugInfo/CXX/windows-dtor.cpp
index beea56ce7368b..ffef45b9f7d1b 100644
--- a/clang/test/DebugInfo/CXX/windows-dtor.cpp
+++ b/clang/test/DebugInfo/CXX/windows-dtor.cpp
@@ -16,7 +16,7 @@ struct AB: A, B {
template struct AB<int>;
// CHECK: define {{.*}}@"??_E?$AB at H@@W3AEPAXI at Z"({{.*}} !dbg [[THUNK_VEC_DEL_DTOR:![0-9]*]]
-// CHECK: call {{.*}}@"??_G?$AB at H@@UAEPAXI at Z"({{.*}}) #{{[0-9]*}}, !dbg [[THUNK_LOC:![0-9]*]]
+// CHECK: call {{.*}}@"??_E?$AB at H@@UAEPAXI at Z"({{.*}}) #{{[0-9]*}}, !dbg [[THUNK_LOC:![0-9]*]]
// CHECK: define
// CHECK: [[THUNK_VEC_DEL_DTOR]] = distinct !DISubprogram
diff --git a/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/module.modulemap b/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/module.modulemap
new file mode 100644
index 0000000000000..bb7ff1c9952c8
--- /dev/null
+++ b/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/module.modulemap
@@ -0,0 +1 @@
+module msvc_vector_deleting_destructors { header "msvc-vector-deleting-dtors.h" export * }
diff --git a/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/msvc-vector-deleting-dtors.h b/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/msvc-vector-deleting-dtors.h
new file mode 100644
index 0000000000000..55492667e39d7
--- /dev/null
+++ b/clang/test/Modules/Inputs/msvc-vector-deleting-dtors/msvc-vector-deleting-dtors.h
@@ -0,0 +1,16 @@
+class Base1 {
+public:
+ void operator delete[](void *);
+};
+class Base2 {
+public:
+ void operator delete(void *);
+};
+struct Derived : Base1, Base2 {
+ virtual ~Derived() {}
+};
+void in_h_tests(Derived *p, Derived *p1) {
+ ::delete[] p;
+
+ delete[] p1;
+}
diff --git a/clang/test/Modules/msvc-vector-deleting-destructors.cpp b/clang/test/Modules/msvc-vector-deleting-destructors.cpp
new file mode 100644
index 0000000000000..a0806054355db
--- /dev/null
+++ b/clang/test/Modules/msvc-vector-deleting-destructors.cpp
@@ -0,0 +1,30 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -x c++ -fmodules-cache-path=%t -I %S/Inputs/msvc-vector-deleting-dtors -emit-llvm -triple=i386-pc-win32 -o - | FileCheck %s --check-prefixes CHECK,CHECK32
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -x c++ -fmodules-cache-path=%t -I %S/Inputs/msvc-vector-deleting-dtors -emit-llvm -triple=x86_64-pc-win32 -o - | FileCheck %s --check-prefixes CHECK,CHECK64
+
+#include "msvc-vector-deleting-dtors.h"
+
+void call_in_module_function(void) {
+ in_h_tests(new Derived[2], new Derived[3]);
+}
+
+void out_of_module_tests(Derived *p, Derived *p1) {
+ ::delete[] p;
+
+ delete[] p1;
+}
+
+// CHECK32-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EDerived@@UAEPAXI at Z"
+// CHECK64-LABEL: define weak dso_local noundef ptr @"??_EDerived@@UEAAPEAXI at Z"
+// CHECK: dtor.call_class_delete_after_array_destroy:
+// CHECK32-NEXT: call void @"??_VBase1@@SAXPAX at Z"(ptr noundef %2)
+// CHECK64-NEXT: call void @"??_VBase1@@SAXPEAX at Z"(ptr noundef %2)
+// CHECK: dtor.call_glob_delete_after_array_destroy:
+// CHECK32-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %2, i32 noundef 8)
+// CHECK64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 16)
+// CHECK: dtor.call_glob_delete:
+// CHECK32-NEXT: call void @"??3 at YAXPAXI@Z"(ptr noundef %this1, i32 noundef 8)
+// CHECK64-NEXT: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %this1, i64 noundef 16)
+// CHECK: dtor.call_class_delete:
+// CHECK32-NEXT: call void @"??3Base2@@SAXPAX at Z"(ptr noundef %this1)
+// CHECK64-NEXT: call void @"??3Base2@@SAXPEAX at Z"(ptr noundef %this1)
diff --git a/clang/test/Modules/vtable-windows.cppm b/clang/test/Modules/vtable-windows.cppm
index dbde24c8a9bdd..e45e32d6b4d60 100644
--- a/clang/test/Modules/vtable-windows.cppm
+++ b/clang/test/Modules/vtable-windows.cppm
@@ -23,4 +23,4 @@ void test() {
// Check that the virtual table is an unnamed_addr constant in comdat that can
// be merged with the virtual table with other TUs.
-// CHECK: unnamed_addr constant {{.*}}[ptr @"??_R4Fruit@@6B@", ptr @"??_GFruit@@UAEPAXI at Z", ptr @"?eval at Fruit@@UAEXXZ"{{.*}}comdat($"??_7Fruit@@6B@")
+// CHECK: unnamed_addr constant {{.*}}[ptr @"??_R4Fruit@@6B@", ptr @"??_EFruit@@UAEPAXI at Z", ptr @"?eval at Fruit@@UAEXXZ"{{.*}}comdat($"??_7Fruit@@6B@")
diff --git a/clang/test/PCH/Inputs/msvc-vector-deleting-dtors.h b/clang/test/PCH/Inputs/msvc-vector-deleting-dtors.h
new file mode 100644
index 0000000000000..55492667e39d7
--- /dev/null
+++ b/clang/test/PCH/Inputs/msvc-vector-deleting-dtors.h
@@ -0,0 +1,16 @@
+class Base1 {
+public:
+ void operator delete[](void *);
+};
+class Base2 {
+public:
+ void operator delete(void *);
+};
+struct Derived : Base1, Base2 {
+ virtual ~Derived() {}
+};
+void in_h_tests(Derived *p, Derived *p1) {
+ ::delete[] p;
+
+ delete[] p1;
+}
diff --git a/clang/test/PCH/msvc-vector-deleting-destructors.cpp b/clang/test/PCH/msvc-vector-deleting-destructors.cpp
new file mode 100644
index 0000000000000..f548dba8efd20
--- /dev/null
+++ b/clang/test/PCH/msvc-vector-deleting-destructors.cpp
@@ -0,0 +1,34 @@
+// Test this without pch.
+// RUN: %clang_cc1 -x c++ -include %S/Inputs/msvc-vector-deleting-dtors.h -emit-llvm -o - %s -triple=i386-pc-win32 | FileCheck %s --check-prefixes CHECK,CHECK32
+// RUN: %clang_cc1 -x c++ -include %S/Inputs/msvc-vector-deleting-dtors.h -emit-llvm -o - %s -triple=x86_64-pc-win32 | FileCheck %s --check-prefixes CHECK,CHECK64
+
+// Test with pch.
+// RUN: %clang_cc1 -x c++ -emit-pch -o %t -triple=i386-pc-win32 %S/Inputs/msvc-vector-deleting-dtors.h
+// RUN: %clang_cc1 -x c++ -include-pch %t -emit-llvm -triple=i386-pc-win32 -o - %s | FileCheck %s --check-prefixes CHECK,CHECK32
+// RUN: %clang_cc1 -x c++ -emit-pch -o %t -triple=x86_64-pc-win32 %S/Inputs/msvc-vector-deleting-dtors.h
+// RUN: %clang_cc1 -x c++ -include-pch %t -emit-llvm -triple=x86_64-pc-win32 -o - %s | FileCheck %s --check-prefixes CHECK,CHECK64
+
+void call_in_module_function(void) {
+ in_h_tests(new Derived[2], new Derived[3]);
+}
+
+void out_of_module_tests(Derived *p, Derived *p1) {
+ ::delete[] p;
+
+ delete[] p1;
+}
+
+// CHECK32-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EDerived@@UAEPAXI at Z"
+// CHECK64-LABEL: define weak dso_local noundef ptr @"??_EDerived@@UEAAPEAXI at Z"
+// CHECK: dtor.call_class_delete_after_array_destroy:
+// CHECK32-NEXT: call void @"??_VBase1@@SAXPAX at Z"(ptr noundef %2)
+// CHECK64-NEXT: call void @"??_VBase1@@SAXPEAX at Z"(ptr noundef %2)
+// CHECK: dtor.call_glob_delete_after_array_destroy:
+// CHECK32-NEXT: call void @"??_V at YAXPAXI@Z"(ptr noundef %2, i32 noundef 8)
+// CHECK64-NEXT: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 16)
+// CHECK: dtor.call_glob_delete:
+// CHECK32-NEXT: call void @"??3 at YAXPAXI@Z"(ptr noundef %this1, i32 noundef 8)
+// CHECK64-NEXT: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %this1, i64 noundef 16)
+// CHECK: dtor.call_class_delete:
+// CHECK32-NEXT: call void @"??3Base2@@SAXPAX at Z"(ptr noundef %this1)
+// CHECK64-NEXT: call void @"??3Base2@@SAXPEAX at Z"(ptr noundef %this1)
diff --git a/clang/test/Profile/cxx-abc-deleting-dtor.cpp b/clang/test/Profile/cxx-abc-deleting-dtor.cpp
index c65a8e8013c35..7c2a5bbc93af3 100644
--- a/clang/test/Profile/cxx-abc-deleting-dtor.cpp
+++ b/clang/test/Profile/cxx-abc-deleting-dtor.cpp
@@ -24,16 +24,15 @@ DerivedABC *useABCVTable() { return new DerivedABC(); }
// MSVC: @"__profn_??1ABC@@{{.*}}" =
// MSVC-NOT: @"__profn_??_G{{.*}}" =
-// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GDerivedABC@@UEAAPEAXI at Z"(ptr {{[^,]*}} %this, {{.*}})
-// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
-// MSVC: call void @"??1DerivedABC@@UEAA at XZ"({{.*}})
-// MSVC: ret void
-
// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GABC@@UEAAPEAXI at Z"(ptr {{[^,]*}} %this, {{.*}})
// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
// MSVC: call void @llvm.trap()
// MSVC-NEXT: unreachable
+// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GDerivedABC@@UEAAPEAXI at Z"(ptr {{[^,]*}} %this, {{.*}})
+// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
+// MSVC: call void @"??1DerivedABC@@UEAA at XZ"({{.*}})
+
// MSVC-LABEL: define linkonce_odr dso_local void @"??1DerivedABC@@UEAA at XZ"({{.*}})
// MSVC: call void @llvm.instrprof.increment({{.*}})
// MSVC: call void @"??1ABC@@UEAA at XZ"({{.*}})
diff --git a/clang/test/SemaCXX/gh134265.cpp b/clang/test/SemaCXX/gh134265.cpp
new file mode 100644
index 0000000000000..790165411c938
--- /dev/null
+++ b/clang/test/SemaCXX/gh134265.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 %s -verify=expected -fsyntax-only -triple=x86_64-unknown-linux-gnu
+// RUN: %clang_cc1 %s -verify=expected -fsyntax-only -triple=x86_64-unknown-linux-gnu -std=c++20
+// RUN: %clang_cc1 %s -verify=expected,ms -fms-extensions -fms-compatibility -triple=x86_64-pc-windows-msvc -DMS
+
+// Verify that clang doesn't emit additional errors when searching for
+// additional operators delete for vector deleting destructors support.
+
+struct Foo {
+ virtual ~Foo() {} // expected-error {{attempt to use a deleted function}}
+ static void operator delete(void* ptr) = delete; // expected-note {{explicitly marked deleted here}}
+};
+
+
+struct Bar {
+ virtual ~Bar() {}
+ static void operator delete[](void* ptr) = delete;
+};
+
+struct Baz {
+ virtual ~Baz() {}
+ static void operator delete[](void* ptr) = delete; // expected-note {{explicitly marked deleted here}}
+};
+
+struct BarBaz {
+ ~BarBaz() {}
+ static void operator delete[](void* ptr) = delete;
+};
+
+void foobar() {
+ Baz *B = new Baz[10]();
+ delete [] B; // expected-error {{attempt to use a deleted function}}
+ BarBaz *BB = new BarBaz[10]();
+}
+
+struct BaseDelete1 {
+ void operator delete[](void *);
+};
+struct BaseDelete2 {
+ void operator delete[](void *);
+};
+struct BaseDestructor {
+ BaseDestructor() {}
+ virtual ~BaseDestructor() = default;
+};
+struct Final : BaseDelete1, BaseDelete2, BaseDestructor {
+ Final() {}
+};
+struct FinalExplicit : BaseDelete1, BaseDelete2, BaseDestructor {
+ FinalExplicit() {}
+ inline ~FinalExplicit() {}
+};
+
+#ifdef MS
+struct Final1 : BaseDelete1, BaseDelete2, BaseDestructor {
+ __declspec(dllexport) ~Final1() {}
+};
+#endif // MS
+
+void foo() {
+ Final* a = new Final[10]();
+ FinalExplicit* b = new FinalExplicit[10]();
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index ca8e74337733c..52eddb337a56a 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2516,6 +2516,7 @@ static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) {
case clang::CXXDtorType::Dtor_Unified:
return "D4";
case clang::CXXDtorType::Dtor_Comdat:
+ case clang::CXXDtorType::Dtor_VectorDeleting:
llvm_unreachable("Unexpected destructor kind.");
}
llvm_unreachable("Fully covered switch above");
>From 08a90c683659000c9cceb7eafb515cf18f60fa4a Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Tue, 2 Dec 2025 09:28:05 -0800
Subject: [PATCH 2/2] Fix issue with /Zc:dllexportInlines-
---
clang/include/clang/AST/ASTContext.h | 5 ++
clang/lib/AST/ASTContext.cpp | 20 +++++++
clang/lib/CodeGen/CGExprCXX.cpp | 10 ----
clang/lib/CodeGen/CodeGenModule.cpp | 50 -----------------
clang/lib/CodeGen/CodeGenModule.h | 5 --
clang/lib/CodeGen/MicrosoftCXXABI.cpp | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 5 ++
clang/lib/Sema/SemaExprCXX.cpp | 13 +++++
.../microsoft-vector-deleting-dtors2.cpp | 56 +++++++++++++++++++
clang/test/DebugInfo/CXX/ms-dtor-thunks.cpp | 6 +-
10 files changed, 103 insertions(+), 69 deletions(-)
create mode 100644 clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 6e9e737dcae4f..303d919448718 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -382,6 +382,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::DenseMap<const CXXDestructorDecl *, FunctionDecl *>
GlobalArrayOperatorDeletesForVirtualDtor;
+ /// To remember which types did require a vector deleting dtor.
+ llvm::DenseSet<const CXXRecordDecl *> RequireVectorDeletingDtor;
+
/// The next string literal "version" to allocate during constant evaluation.
/// This is used to distinguish between repeated evaluations of the same
/// string literal.
@@ -3494,6 +3497,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
OperatorDeleteKind K) const;
bool dtorHasOperatorDelete(const CXXDestructorDecl *Dtor,
OperatorDeleteKind K) const;
+ void setClassNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);
+ bool classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD);
/// Retrieve the context for computing mangling numbers in the given
/// DeclContext.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b929b0fc1aa8e..f508291a91bca 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13402,6 +13402,26 @@ ASTContext::getOperatorDeleteForVDtor(const CXXDestructorDecl *Dtor,
return nullptr;
}
+bool ASTContext::classNeedsVectorDeletingDestructor(const CXXRecordDecl *RD) {
+ if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
+ return false;
+ CXXDestructorDecl *Dtor = RD->getDestructor();
+ // The compiler can't know if new[]/delete[] will be used outside of the DLL,
+ // so just force vector deleting destructor emission if dllexport is present.
+ // This matches MSVC behavior.
+ if (Dtor && Dtor->isVirtual() && Dtor->hasAttr<DLLExportAttr>())
+ return true;
+
+ return RequireVectorDeletingDtor.count(RD);
+}
+
+void ASTContext::setClassNeedsVectorDeletingDestructor(
+ const CXXRecordDecl *RD) {
+ if (!getTargetInfo().emitVectorDeletingDtors(getLangOpts()))
+ return;
+ RequireVectorDeletingDtor.insert(RD);
+}
+
MangleNumberingContext &
ASTContext::getManglingNumberContext(const DeclContext *DC) {
assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index f64cf9f8a6c2d..530312821b96d 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1206,16 +1206,6 @@ void CodeGenFunction::EmitNewArrayInitializer(
EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, CCE,
/*NewPointerIsChecked*/true,
CCE->requiresZeroInitialization());
-
- // For MSVC vector deleting destructors support we record that for the class
- // new[] was called. We try to optimize the code size and only emit vector
- // deleting destructors when they are required. Vector deleting destructors
- // are required for delete[] call but MSVC triggers emission of them
- // whenever new[] is called for an object of the class and we do the same
- // for compatibility.
- if (CGM.getContext().getTargetInfo().emitVectorDeletingDtors(
- CGM.getContext().getLangOpts()))
- CGM.requireVectorDestructorDefinition(Ctor->getParent());
return;
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index ddfba1a9a3734..4789c6b26797f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -8323,53 +8323,3 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) {
NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx);
}
-
-bool CodeGenModule::classNeedsVectorDestructor(const CXXRecordDecl *RD) {
- if (!Context.getTargetInfo().emitVectorDeletingDtors(Context.getLangOpts()))
- return false;
- CXXDestructorDecl *Dtor = RD->getDestructor();
- // The compiler can't know if new[]/delete[] will be used outside of the DLL,
- // so just force vector deleting destructor emission if dllexport is present.
- // This matches MSVC behavior.
- if (Dtor && Dtor->isVirtual() && Dtor->isDefined() &&
- Dtor->hasAttr<DLLExportAttr>())
- return true;
-
- return RequireVectorDeletingDtor.count(RD);
-}
-
-void CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
- if (!Context.getTargetInfo().emitVectorDeletingDtors(Context.getLangOpts()))
- return;
- RequireVectorDeletingDtor.insert(RD);
-
- // To reduce code size in general case we lazily emit scalar deleting
- // destructor definition and an alias from vector deleting destructor to
- // scalar deleting destructor. It may happen that we first emitted the scalar
- // deleting destructor definition and the alias and then discovered that the
- // definition of the vector deleting destructor is required. Then we need to
- // remove the alias and the scalar deleting destructor and queue vector
- // deleting destructor body for emission. Check if that is the case.
- CXXDestructorDecl *DtorD = RD->getDestructor();
- GlobalDecl ScalarDtorGD(DtorD, Dtor_Deleting);
- StringRef MangledName = getMangledName(ScalarDtorGD);
- llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
- if (Entry && !Entry->isDeclaration()) {
- GlobalDecl VectorDtorGD(DtorD, Dtor_VectorDeleting);
- StringRef VDName = getMangledName(VectorDtorGD);
- llvm::GlobalValue *VDEntry = GetGlobalValue(VDName);
- // It exists and it should be an alias.
- assert(VDEntry && isa<llvm::GlobalAlias>(VDEntry));
- auto *NewFn = llvm::Function::Create(
- cast<llvm::FunctionType>(VDEntry->getValueType()),
- llvm::Function::ExternalLinkage, VDName, &getModule());
- SetFunctionAttributes(VectorDtorGD, NewFn, /*IsIncompleteFunction*/ false,
- /*IsThunk*/ false);
- NewFn->takeName(VDEntry);
- VDEntry->replaceAllUsesWith(NewFn);
- VDEntry->eraseFromParent();
- Entry->replaceAllUsesWith(NewFn);
- Entry->eraseFromParent();
- addDeferredDeclToEmit(VectorDtorGD);
- }
-}
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 2acfc83338a0c..e36ad87e67e3b 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -529,9 +529,6 @@ class CodeGenModule : public CodeGenTypeCache {
/// that we don't re-emit the initializer.
llvm::DenseMap<const Decl*, unsigned> DelayedCXXInitPosition;
- /// To remember which types did require a vector deleting dtor.
- llvm::SmallPtrSet<const CXXRecordDecl *, 16> RequireVectorDeletingDtor;
-
typedef std::pair<OrderGlobalInitsOrStermFinalizers, llvm::Function *>
GlobalInitData;
@@ -1828,8 +1825,6 @@ class CodeGenModule : public CodeGenTypeCache {
// behavior. So projects like the Linux kernel can rely on it.
return !getLangOpts().CPlusPlus;
}
- void requireVectorDestructorDefinition(const CXXRecordDecl *RD);
- bool classNeedsVectorDestructor(const CXXRecordDecl *RD);
// Helper to get the alignment for a variable.
unsigned getVtableGlobalVarAlignment(const VarDecl *D = nullptr) {
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 11ca94f03cb98..edefaff0ca1ac 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -4105,7 +4105,7 @@ void MicrosoftCXXABI::emitCXXStructor(GlobalDecl GD) {
return;
if (GD.getDtorType() == Dtor_VectorDeleting &&
- !CGM.classNeedsVectorDestructor(dtor->getParent())) {
+ !getContext().classNeedsVectorDeletingDestructor(dtor->getParent())) {
// Create GlobalDecl object with the correct type for the scalar
// deleting destructor.
GlobalDecl ScalarDtorGD(dtor, Dtor_Deleting);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ceae8d951796c..3c029279b731e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11193,6 +11193,7 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false,
/*LookForGlobal*/ true, Name);
Destructor->setOperatorGlobalDelete(GlobalOperatorDelete);
+ MarkFunctionReferenced(Loc, GlobalOperatorDelete);
}
if (Context.getTargetInfo().emitVectorDeletingDtors(
@@ -11209,6 +11210,8 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
/*LookForGlobal*/ true,
VDeleteName);
Destructor->setGlobalOperatorArrayDelete(GlobalArrOperatorDelete);
+ if (Context.classNeedsVectorDeletingDestructor(RD))
+ MarkFunctionReferenced(Loc, GlobalArrOperatorDelete);
} else if (!ArrOperatorDelete) {
ArrOperatorDelete = FindDeallocationFunctionForDestructor(
Loc, RD, /*Diagnose*/ false,
@@ -11217,6 +11220,8 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
assert(ArrOperatorDelete &&
"Should've found at least global array delete");
Destructor->setOperatorArrayDelete(ArrOperatorDelete);
+ if (Context.classNeedsVectorDeletingDestructor(RD))
+ MarkFunctionReferenced(Loc, ArrOperatorDelete);
}
}
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d2388b4f9b8dc..6bc100233d967 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2631,6 +2631,19 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
MarkFunctionReferenced(StartLoc, OperatorDelete);
}
+ // For MSVC vector deleting destructors support we record that for the class
+ // new[] was called. We try to optimize the code size and only emit vector
+ // deleting destructors when they are required. Vector deleting destructors
+ // are required for delete[] call but MSVC triggers emission of them
+ // whenever new[] is called for an object of the class and we do the same
+ // for compatibility.
+ if (const CXXConstructExpr *CCE =
+ dyn_cast_or_null<CXXConstructExpr>(Initializer);
+ CCE && ArraySize) {
+ Context.setClassNeedsVectorDeletingDestructor(
+ CCE->getConstructor()->getParent());
+ }
+
return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
IAP, UsualArrayDeleteWantsSize, PlacementArgs,
TypeIdParens, ArraySize, InitStyle, Initializer,
diff --git a/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
new file mode 100644
index 0000000000000..bb426f6280130
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -emit-llvm -fms-extensions -fms-compatibility -fno-dllexport-inlines %s -triple=x86_64-pc-windows-msvc -o - | FileCheck --check-prefixes=X64,CHECK %s
+// RUN: %clang_cc1 -emit-llvm -fms-extensions -fms-compatibility -fno-dllexport-inlines %s -triple=i386-pc-windows-msvc -o - | FileCheck --check-prefixes=X86,CHECK %s
+
+// Check that vector deleting destructor does not reference undefined symbols.
+
+void operator delete(void*, size_t) {}
+void operator delete[](void*, size_t) {}
+
+template <typename T> struct RefCounted {
+ void operator delete[](void *p) { }
+};
+
+struct __declspec(dllexport) DrawingBuffer : public RefCounted<DrawingBuffer> {
+ DrawingBuffer();
+ virtual ~DrawingBuffer();
+};
+
+DrawingBuffer::DrawingBuffer() {}
+DrawingBuffer::~DrawingBuffer() {}
+
+struct NoExport : public RefCounted<NoExport> {
+ NoExport();
+ virtual ~NoExport();
+};
+
+NoExport::NoExport() {}
+NoExport::~NoExport() {}
+
+// X64: define dso_local void @"??3 at YAXPEAX_K@Z"(ptr noundef %0, i64 noundef %1)
+// X64: define dso_local void @"??_V at YAXPEAX_K@Z"(ptr noundef %0, i64 noundef %1)
+// X64: define dso_local dllexport void @"??1DrawingBuffer@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %this)
+
+// X64: define weak dso_local noundef ptr @"??_EDrawingBuffer@@UEAAPEAXI at Z"
+// X64: call void @"??1DrawingBuffer@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)
+// X64: call void @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPEAX at Z"(ptr noundef %2) #2
+// X64: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 8) #3
+// X64: call void @"??1DrawingBuffer@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %this1)
+// X64: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %this1, i64 noundef 8) #3
+
+// X64: define linkonce_odr dso_local void @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPEAX at Z"(ptr noundef %p)
+
+// X86: define dso_local void @"??3 at YAXPAXI@Z"(ptr noundef %0, i32 noundef %1)
+// X86: define dso_local void @"??_V at YAXPAXI@Z"(ptr noundef %0, i32 noundef %1)
+
+// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_EDrawingBuffer@@UAEPAXI at Z"
+// X86: call x86_thiscallcc void @"??1DrawingBuffer@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %arraydestroy.element)
+// X86: call void @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPAX at Z"(ptr noundef %2)
+// X86: call void @"??_V at YAXPAXI@Z"(ptr noundef %2, i32 noundef 4)
+// X86 call x86_thiscallcc void @"??1DrawingBuffer@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %this1)
+// X86: call void @"??3 at YAXPAXI@Z"(ptr noundef %this1, i32 noundef 4)
+
+// X86: define linkonce_odr dso_local void @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPAX at Z"(ptr noundef %p)
+
+// X86: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GNoExport@@UAEPAXI at Z"(ptr noundef nonnull align 4 dereferenceable(4) %this, i32 noundef %should_call_delete)
+// X64: define linkonce_odr dso_local noundef ptr @"??_GNoExport@@UEAAPEAXI at Z"(ptr noundef nonnull align 8 dereferenceable(8) %this, i32 noundef %should_call_delete)
+// CHECK-NOT: define {{.*}}_V{{.*}}NoExport
diff --git a/clang/test/DebugInfo/CXX/ms-dtor-thunks.cpp b/clang/test/DebugInfo/CXX/ms-dtor-thunks.cpp
index 701874580d07d..d6294cb32bf7c 100644
--- a/clang/test/DebugInfo/CXX/ms-dtor-thunks.cpp
+++ b/clang/test/DebugInfo/CXX/ms-dtor-thunks.cpp
@@ -5,11 +5,11 @@ struct __declspec(dllexport) S { virtual ~S(); };
struct __declspec(dllexport) T { virtual ~T(); };
struct __declspec(dllexport) U : S, T { virtual ~U(); };
-// CHECK-LABEL: define {{.*}} @"??_GS@@UAEPAXI at Z"
+// CHECK-LABEL: define {{.*}} @"??_ES@@UAEPAXI at Z"
// CHECK: call x86_thiscallcc void @"??1S@@UAE at XZ"(ptr {{[^,]*}} %this1){{.*}}!dbg !{{[0-9]+}}
-// CHECK-LABEL: define {{.*}} @"??_GT@@UAEPAXI at Z"
+// CHECK-LABEL: define {{.*}} @"??_ET@@UAEPAXI at Z"
// CHECK: call x86_thiscallcc void @"??1T@@UAE at XZ"(ptr {{[^,]*}} %this1){{.*}}!dbg !{{[0-9]+}}
-// CHECK-LABEL: define {{.*}} @"??_GU@@UAEPAXI at Z"
+// CHECK-LABEL: define {{.*}} @"??_EU@@UAEPAXI at Z"
// CHECK: call x86_thiscallcc void @"??1U@@UAE at XZ"(ptr {{[^,]*}} %this1){{.*}}!dbg !{{[0-9]+}}
More information about the lldb-commits
mailing list