[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
Wed Dec 10 08:54:53 PST 2025
https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/170337
>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/3] 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/3] 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]+}}
>From dfd63c0fb8fafc7e0dfe09e81d56ae4406e38220 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Wed, 10 Dec 2025 08:53:57 -0800
Subject: [PATCH 3/3] Frame MarkFunctionReferenced calls with null checks
Allow non existent "global" operator delete.
---
clang/lib/CodeGen/CGClass.cpp | 53 ++++++++++---------
clang/lib/Sema/SemaDeclCXX.cpp | 13 ++---
.../microsoft-vector-deleting-dtors2.cpp | 50 +++++++++++++++--
3 files changed, 80 insertions(+), 36 deletions(-)
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 955c606cfc733..033cc0512ef93 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1499,32 +1499,33 @@ static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
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));
+ if (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);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3c029279b731e..3a8979e4b7558 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11192,8 +11192,10 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
FunctionDecl *GlobalOperatorDelete =
FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false,
/*LookForGlobal*/ true, Name);
- Destructor->setOperatorGlobalDelete(GlobalOperatorDelete);
- MarkFunctionReferenced(Loc, GlobalOperatorDelete);
+ if (GlobalOperatorDelete) {
+ MarkFunctionReferenced(Loc, GlobalOperatorDelete);
+ Destructor->setOperatorGlobalDelete(GlobalOperatorDelete);
+ }
}
if (Context.getTargetInfo().emitVectorDeletingDtors(
@@ -11210,17 +11212,16 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
/*LookForGlobal*/ true,
VDeleteName);
Destructor->setGlobalOperatorArrayDelete(GlobalArrOperatorDelete);
- if (Context.classNeedsVectorDeletingDestructor(RD))
+ if (GlobalArrOperatorDelete &&
+ Context.classNeedsVectorDeletingDestructor(RD))
MarkFunctionReferenced(Loc, 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);
- if (Context.classNeedsVectorDeletingDestructor(RD))
+ if (ArrOperatorDelete && Context.classNeedsVectorDeletingDestructor(RD))
MarkFunctionReferenced(Loc, ArrOperatorDelete);
}
}
diff --git a/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
index bb426f6280130..87b384c454978 100644
--- a/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
+++ b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors2.cpp
@@ -2,6 +2,8 @@
// 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.
+// Check that when there is no suitable operators delete for vector deleting
+// destructor, we still emit it without errors.
void operator delete(void*, size_t) {}
void operator delete[](void*, size_t) {}
@@ -26,18 +28,41 @@ struct NoExport : public RefCounted<NoExport> {
NoExport::NoExport() {}
NoExport::~NoExport() {}
+namespace std {
+ template <class T> struct type_identity {
+ typedef T type;
+ };
+ enum class align_val_t : __SIZE_TYPE__ {};
+ struct destroying_delete_t { explicit destroying_delete_t() = default; };
+}
+using size_t = __SIZE_TYPE__;
+
+struct Test {
+ void operator delete(void *) ;
+ virtual ~Test();
+};
+
+void *operator new(std::type_identity<Test>, size_t, std::align_val_t) throw();
+void operator delete(std::type_identity<Test>, void*, size_t, std::align_val_t) = delete;
+void *operator new[](std::type_identity<Test>, size_t, std::align_val_t) throw();
+void operator delete[](std::type_identity<Test>, void*, size_t, std::align_val_t) = delete;
+
+void TesttheTest() {
+
+ Test *a = new Test[30];
+}
+
// 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 @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPEAX at Z"(ptr noundef %2)
+// X64: call void @"??_V at YAXPEAX_K@Z"(ptr noundef %2, i64 noundef 8)
// 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: call void @"??3 at YAXPEAX_K@Z"(ptr noundef %this1, i64 noundef 8)
-// 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)
@@ -49,6 +74,23 @@ NoExport::~NoExport() {}
// 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)
+
+// X64: define weak dso_local noundef ptr @"??_ETest@@UEAAPEAXI at Z"(ptr noundef nonnull align 8 dereferenceable(8) %this, i32 noundef %should_call_delete)
+// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_ETest@@UAEPAXI at Z"(ptr noundef nonnull align 4 dereferenceable(4) %this, i32 noundef %should_call_delete)
+// CHECK: dtor.call_delete_after_array_destroy:
+// CHECK-NEXT: br label %dtor.continue
+// CHECK: dtor.scalar:
+// X64-NEXT: call void @"??1Test@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %this1)
+// X86-NEXT: call x86_thiscallcc void @"??1Test@@UAE at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %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 @"??3Test@@SAXPEAX at Z"(ptr noundef %this1)
+// X86-NEXT: call void @"??3Test@@SAXPAX at Z"(ptr noundef %this1)
+// CHECK-NEXT: br label %dtor.continue
+
+// X64: define linkonce_odr dso_local void @"??_V?$RefCounted at UDrawingBuffer@@@@SAXPEAX at Z"(ptr noundef %p)
// 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)
More information about the lldb-commits
mailing list