[clang] [MS][clang] Add support for vector deleting destructors (PR #126240)

Mariya Podchishchaeva via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 20 10:46:20 PST 2025


https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/126240

>From 04636bea1b873805af02dea865637d7125cee1e5 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 12 Dec 2024 08:57:37 -0800
Subject: [PATCH 1/5] [MS][clang] Add support for vector deleting destructors

Whereas it is UB in terms of the standard to delete an array of objects via
pointer whose static type doesn't match its dynamic type, MSVC supports
an extension allowing to do it.
Aside from array deletion not working correctly in the mentioned case, currently
not having this extension implemented causes clang to generate code that is not
compatible with the code generated by MSVC, because clang always
puts scalar deleting destructor to the vftable. This PR aims to resolve
these problems.

Fixes https://github.com/llvm/llvm-project/issues/19772
---
 clang/include/clang/AST/VTableBuilder.h       |   6 +-
 clang/include/clang/Basic/ABI.h               |   9 +-
 clang/lib/AST/ItaniumMangle.cpp               |   2 +
 clang/lib/AST/MicrosoftMangle.cpp             |  18 +--
 clang/lib/AST/VTableBuilder.cpp               |  18 ++-
 clang/lib/CodeGen/CGCXX.cpp                   |  37 +++++-
 clang/lib/CodeGen/CGCXXABI.cpp                |  14 +++
 clang/lib/CodeGen/CGCXXABI.h                  |  23 ++--
 clang/lib/CodeGen/CGClass.cpp                 |  97 ++++++++++++++--
 clang/lib/CodeGen/CGDebugInfo.cpp             |   3 +-
 clang/lib/CodeGen/CGExprCXX.cpp               |  50 +++++++-
 clang/lib/CodeGen/CGVTables.cpp               |   3 +-
 clang/lib/CodeGen/CodeGenModule.cpp           |  35 ++++++
 clang/lib/CodeGen/CodeGenModule.h             |   6 +
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  27 +++--
 clang/lib/CodeGen/MicrosoftCXXABI.cpp         |  73 ++++++++----
 .../CodeGenCXX/debug-info-windows-dtor.cpp    |   2 +-
 clang/test/CodeGenCXX/dllexport.cpp           |   2 +-
 .../microsoft-abi-extern-template.cpp         |   2 +-
 .../CodeGenCXX/microsoft-abi-structors.cpp    |   5 +-
 .../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       | 108 ++++++++++++++++++
 clang/test/CodeGenCXX/vtable-consteval.cpp    |   4 +-
 clang/test/Modules/vtable-windows.cppm        |   2 +-
 clang/test/Profile/cxx-abc-deleting-dtor.cpp  |   9 +-
 33 files changed, 528 insertions(+), 157 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp

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 231bad799a42c..48969e4f295c3 100644
--- a/clang/include/clang/Basic/ABI.h
+++ b/clang/include/clang/Basic/ABI.h
@@ -31,10 +31,11 @@ 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_Deleting,      ///< Deleting dtor
+  Dtor_Complete,      ///< Complete object dtor
+  Dtor_Base,          ///< Base object dtor
+  Dtor_Comdat,        ///< The COMDAT used for dtors
+  Dtor_VectorDeleting ///< Vector deleting dtor
 };
 
 } // end namespace clang
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e5eb22eae7dd1..3209c1777fc7f 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6001,6 +6001,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 fe34251688a98..d5dc7833a7fcc 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1484,8 +1484,7 @@ 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");
   }
@@ -2886,9 +2885,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
+      // 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) {
+      if (StructorType == Dtor_Deleting ||
+          StructorType == Dtor_VectorDeleting) {
         Out << (PointersAre64Bit ? "PEAXI at Z" : "PAXI at Z");
         return;
       }
@@ -3861,10 +3863,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 fa3055dd1206f..9b7f72e2390f4 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -1733,8 +1733,8 @@ void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables(
       const CXXMethodDecl *MD = I.first;
       const MethodInfo &MI = I.second;
       if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
-        MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)]
-            = MI.VTableIndex - AddressPoint;
+        MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
+            MI.VTableIndex - AddressPoint;
         MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
             = MI.VTableIndex + 1 - AddressPoint;
       } else {
@@ -2655,7 +2655,10 @@ 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;
+        if (!Context.getTargetInfo().getCXXABI().isMicrosoft())
+          MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
+        else
+          MethodVFTableLocations[GlobalDecl(DD, Dtor_VectorDeleting)] = Loc;
       } else {
         MethodVFTableLocations[MD] = Loc;
       }
@@ -3285,7 +3288,10 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
       const CXXDestructorDecl *DD = Component.getDestructorDecl();
 
       DD->printQualifiedName(Out);
-      Out << "() [scalar deleting]";
+      if (Context.getTargetInfo().getCXXABI().isMicrosoft())
+        Out << "() [vector deleting]";
+      else
+        Out << "() [scalar deleting]";
 
       if (DD->isPureVirtual())
         Out << " [pure]";
@@ -3756,7 +3762,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;
     }
@@ -3873,7 +3879,7 @@ 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 = GD.getCanonicalDecl();
 
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index 78a7b021855b7..6f47e24eed5b3 100644
--- a/clang/lib/CodeGen/CGCXX.cpp
+++ b/clang/lib/CodeGen/CGCXX.cpp
@@ -175,7 +175,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());
@@ -201,6 +200,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 7c6dfc3e59d8c..e109fd0c443f6 100644
--- a/clang/lib/CodeGen/CGCXXABI.cpp
+++ b/clang/lib/CodeGen/CGCXXABI.cpp
@@ -273,6 +273,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 687ff7fb84444..fbbe8f6c6142c 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -251,9 +251,10 @@ class CGCXXABI {
 
 public:
   virtual void emitVirtualObjectDelete(CodeGenFunction &CGF,
-                                       const CXXDeleteExpr *DE,
-                                       Address Ptr, QualType ElementType,
-                                       const CXXDestructorDecl *Dtor) = 0;
+                                       const CXXDeleteExpr *DE, Address Ptr,
+                                       QualType ElementType,
+                                       const CXXDestructorDecl *Dtor,
+                                       bool ArrayDeletion) = 0;
   virtual void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) = 0;
   virtual void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) = 0;
   virtual llvm::GlobalVariable *getThrowInfo(QualType T) { return nullptr; }
@@ -275,6 +276,7 @@ class CGCXXABI {
   virtual CatchTypeInfo getCatchAllTypeInfo();
 
   virtual bool shouldTypeidBeNullChecked(QualType SrcRecordTy) = 0;
+  virtual bool hasVectorDeletingDtors() = 0;
   virtual void EmitBadTypeidCall(CodeGenFunction &CGF) = 0;
   virtual llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
                                   Address ThisPtr,
@@ -485,11 +487,10 @@ class CGCXXABI {
       llvm::PointerUnion<const CXXDeleteExpr *, const CXXMemberCallExpr *>;
 
   /// Emit the ABI-specific virtual destructor call.
-  virtual llvm::Value *
-  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
-                            CXXDtorType DtorType, Address This,
-                            DeleteOrMemberCallExpr E,
-                            llvm::CallBase **CallOrInvoke) = 0;
+  virtual llvm::Value *EmitVirtualDestructorCall(
+      CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
+      Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
+      bool ArrayDeletion = false) = 0;
 
   virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
                                                 GlobalDecl GD,
@@ -575,6 +576,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.
+  virtual 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 7a1096fcbca82..bb94d502c34fa 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1433,6 +1433,83 @@ static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
   return true;
 }
 
+namespace {
+llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
+                                   const CXXDestructorDecl *DD) {
+  if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
+    return CGF.EmitScalarExpr(ThisArg);
+  return CGF.LoadCXXThis();
+}
+}
+
+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::Constant::getIntegerValue(CondTy, llvm::APInt(CondTy->getBitWidth(),
+                                                          /*val=*/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::Constant::getIntegerValue(CondTy, llvm::APInt(CondTy->getBitWidth(),
+                                                          /*val=*/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();
+  CGF.EmitDeleteCall(Dtor->getOperatorDelete(), allocatedPtr,
+                     CGF.getContext().getTagDeclType(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());
@@ -1462,7 +1539,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()) {
@@ -1491,6 +1570,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
   switch (DtorType) {
   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()) &&
@@ -1567,13 +1647,6 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args)
 }
 
 namespace {
-  llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
-                                     const CXXDestructorDecl *DD) {
-    if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
-      return CGF.EmitScalarExpr(ThisArg);
-    return CGF.LoadCXXThis();
-  }
-
   /// Call the operator delete associated with the current destructor.
   struct CallDtorDelete final : EHScopeStack::Cleanup {
     CallDtorDelete() {}
@@ -1592,8 +1665,12 @@ namespace {
                                      bool ReturnAfterDelete) {
     llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
     llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
-    llvm::Value *ShouldCallDelete
-      = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
+    auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
+    llvm::Value *CheckTheBit = CGF.Builder.CreateAnd(
+        ShouldDeleteCondition, llvm::Constant::getIntegerValue(
+                                   CondTy, llvm::APInt(CondTy->getBitWidth(),
+                                                       /*val=*/1)));
+    llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(CheckTheBit);
     CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
 
     CGF.EmitBlock(callDeleteBB);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index d5b584ec0f2e9..da514ff612a32 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2104,7 +2104,8 @@ 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, Dtor_VectorDeleting) : 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 f71c18a8041b1..5b00f79b03293 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1209,6 +1209,8 @@ void CodeGenFunction::EmitNewArrayInitializer(
     EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, CCE,
                                /*NewPointerIsChecked*/true,
                                CCE->requiresZeroInitialization());
+    if (CGM.getCXXABI().hasVectorDeletingDtors())
+      CGM.requireVectorDestructorDefinition(Ctor->getParent());
     return;
   }
 
@@ -1902,8 +1904,8 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
                                        QualType ElementType) {
   auto *Dtor = ElementType->getAsCXXRecordDecl()->getDestructor();
   if (Dtor && Dtor->isVirtual())
-    CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
-                                                Dtor);
+    CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType, Dtor,
+                                                /*ArrayDeletion=*/false);
   else
     CGF.EmitDeleteCall(DE->getOperatorDelete(), Ptr.emitRawPointer(CGF),
                        ElementType);
@@ -1916,7 +1918,8 @@ static bool EmitObjectDelete(CodeGenFunction &CGF,
                              const CXXDeleteExpr *DE,
                              Address Ptr,
                              QualType ElementType,
-                             llvm::BasicBlock *UnconditionalDeleteBlock) {
+                             llvm::BasicBlock *UnconditionalDeleteBlock,
+                             bool ArrayDeletion) {
   // C++11 [expr.delete]p3:
   //   If the static type of the object to be deleted is different from its
   //   dynamic type, the static type shall be a base class of the dynamic type
@@ -1961,7 +1964,7 @@ static bool EmitObjectDelete(CodeGenFunction &CGF,
         }
         if (UseVirtualCall) {
           CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
-                                                      Dtor);
+                                                      Dtor, ArrayDeletion);
           return false;
         }
       }
@@ -2131,11 +2134,48 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
 
   assert(ConvertTypeForMem(DeleteTy) == Ptr.getElementType());
 
+  if (E->isArrayForm() && CGM.getCXXABI().hasVectorDeletingDtors()) {
+    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 0 length. 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::Constant::getIntegerValue(
+                             CondTy, llvm::APInt(CondTy->getBitWidth(),
+                                                 /*val=*/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,
+                              /*ArrayDeletion*/true))
+          EmitBlock(DeleteEnd);
+        return;
+      }
+    }
+  }
+
   if (E->isArrayForm()) {
     EmitArrayDelete(*this, E, Ptr, DeleteTy);
     EmitBlock(DeleteEnd);
   } else {
-    if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
+    if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd, E->isArrayForm()))
       EmitBlock(DeleteEnd);
   }
 }
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 7faf6821a6cdc..0c09a6efb2aa9 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -769,7 +769,8 @@ 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.getCXXABI().hasVectorDeletingDtors());
 
     if (CGM.getLangOpts().CUDA) {
       // Emit NULL for methods we can't codegen on this
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 47c03ea5e72cb..f38cd8f364fff 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7919,3 +7919,38 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) {
 
   NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx);
 }
+
+bool CodeGenModule::classNeedsVectorDestructor(const CXXRecordDecl *RD) {
+  CXXDestructorDecl *Dtor = RD->getDestructor();
+  if (Dtor && Dtor->isVirtual() && Dtor->isDefined() &&
+      Dtor->hasAttr<DLLExportAttr>())
+    return true;
+
+  assert(getCXXABI().hasVectorDeletingDtors());
+  return RequireVectorDeletingDtor.count(RD);
+}
+
+void CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
+  assert(getCXXABI().hasVectorDeletingDtors());
+  RequireVectorDeletingDtor.insert(RD);
+  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());
+    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 0956296e2d5d8..9cedd350f1a51 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -528,6 +528,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;
 
@@ -1535,6 +1538,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);
 
@@ -1795,6 +1799,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);
 
 private:
   bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 7375a511809b9..ea1ec6cbc1e72 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -90,6 +90,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
 
       case Dtor_Comdat:
         llvm_unreachable("emitting dtor comdat as function?");
+      case Dtor_VectorDeleting:
+        llvm_unreachable("unexpected dtor kind for this ABI");
       }
       llvm_unreachable("bad dtor kind");
     }
@@ -159,7 +161,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
 
   void emitVirtualObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
                                Address Ptr, QualType ElementType,
-                               const CXXDestructorDecl *Dtor) override;
+                               const CXXDestructorDecl *Dtor,
+                               bool ArrayDeletion = false) override;
 
   void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override;
   void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;
@@ -179,6 +182,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
   }
 
   bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
+  bool hasVectorDeletingDtors() override {
+    return false;
+  }
   void EmitBadTypeidCall(CodeGenFunction &CGF) override;
   llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
                           Address ThisPtr,
@@ -314,11 +320,12 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
                                      Address This, llvm::Type *Ty,
                                      SourceLocation Loc) override;
 
-  llvm::Value *
-  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
-                            CXXDtorType DtorType, Address This,
-                            DeleteOrMemberCallExpr E,
-                            llvm::CallBase **CallOrInvoke) override;
+  llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
+                                         const CXXDestructorDecl *Dtor,
+                                         CXXDtorType DtorType, Address This,
+                                         DeleteOrMemberCallExpr E,
+                                         llvm::CallBase **CallOrInvoke,
+                                         bool ArrayDeletion = false) override;
 
   void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
 
@@ -448,7 +455,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
        if (!IsInlined)
          continue;
 
-       StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl());
+       StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl(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
@@ -1368,7 +1375,8 @@ void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
                                             const CXXDeleteExpr *DE,
                                             Address Ptr,
                                             QualType ElementType,
-                                            const CXXDestructorDecl *Dtor) {
+                                            const CXXDestructorDecl *Dtor,
+                                            bool ArrayDeletion) {
   bool UseGlobalDelete = DE->isGlobalDelete();
   if (UseGlobalDelete) {
     // Derive the complete-object pointer, which is what we need
@@ -2238,7 +2246,8 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
 
 llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
     CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
-    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke) {
+    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
+    bool ArrayDeletion) {
   auto *CE = dyn_cast<const CXXMemberCallExpr *>(E);
   auto *D = dyn_cast<const CXXDeleteExpr *>(E);
   assert((CE != nullptr) ^ (D != nullptr));
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 4a2630e83b62d..daeb19071df36 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -70,8 +70,8 @@ class MicrosoftCXXABI : public CGCXXABI {
       switch (GD.getDtorType()) {
       case Dtor_Complete:
       case Dtor_Deleting:
+      case Dtor_VectorDeleting:
         return true;
-
       case Dtor_Base:
         return false;
 
@@ -120,7 +120,8 @@ class MicrosoftCXXABI : public CGCXXABI {
 
   void emitVirtualObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
                                Address Ptr, QualType ElementType,
-                               const CXXDestructorDecl *Dtor) override;
+                               const CXXDestructorDecl *Dtor,
+                               bool ArrayDeletion) override;
 
   void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override;
   void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;
@@ -145,6 +146,9 @@ class MicrosoftCXXABI : public CGCXXABI {
   }
 
   bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
+  bool hasVectorDeletingDtors() override {
+    return true;
+  }
   void EmitBadTypeidCall(CodeGenFunction &CGF) override;
   llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
                           Address ThisPtr,
@@ -260,7 +264,7 @@ 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, Dtor_VectorDeleting);
       }
       MethodVFTableLocation ML =
           CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -334,16 +338,17 @@ class MicrosoftCXXABI : public CGCXXABI {
                                      Address This, llvm::Type *Ty,
                                      SourceLocation Loc) override;
 
-  llvm::Value *
-  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
-                            CXXDtorType DtorType, Address This,
-                            DeleteOrMemberCallExpr E,
-                            llvm::CallBase **CallOrInvoke) override;
+  llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
+                                         const CXXDestructorDecl *Dtor,
+                                         CXXDtorType DtorType, Address This,
+                                         DeleteOrMemberCallExpr E,
+                                         llvm::CallBase **CallOrInvoke,
+                                         bool ArrayDeletion) override;
 
   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 &&
+           "Only vector deleting destructor thunks are available in this ABI");
     CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
                  getContext().IntTy);
   }
@@ -888,15 +893,16 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
 
 void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
                                               const CXXDeleteExpr *DE,
-                                              Address Ptr,
-                                              QualType ElementType,
-                                              const CXXDestructorDecl *Dtor) {
+                                              Address Ptr, QualType ElementType,
+                                              const CXXDestructorDecl *Dtor,
+                                              bool ArrayDeletion) {
   // FIXME: Provide a source location here even though there's no
   // CXXMemberCallExpr for dtor call.
   bool UseGlobalDelete = DE->isGlobalDelete();
   CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
-  llvm::Value *MDThis = EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
-                                                  /*CallOrInvoke=*/nullptr);
+  llvm::Value *MDThis =
+      EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
+                                /*CallOrInvoke=*/nullptr, ArrayDeletion);
   if (UseGlobalDelete)
     CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType);
 }
@@ -1090,7 +1096,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 {
@@ -1341,7 +1348,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;
@@ -1373,7 +1381,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;
@@ -1407,6 +1415,8 @@ llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
     // and are emitted everywhere they are used. They are internal if the class
     // is internal.
     return llvm::GlobalValue::LinkOnceODRLinkage;
+  case Dtor_VectorDeleting:
+    return llvm::GlobalValue::WeakAnyLinkage;
   case Dtor_Comdat:
     llvm_unreachable("MS C++ ABI does not support comdat dtors");
   }
@@ -1438,7 +1448,7 @@ 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, Dtor_VectorDeleting);
   }
 
   MethodVFTableLocation ML =
@@ -1487,7 +1497,7 @@ 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, Dtor_VectorDeleting);
   }
   MethodVFTableLocation ML =
       CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -1995,16 +2005,18 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
 
 llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
     CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
-    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke) {
+    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
+    bool ArrayDeletion) {
   auto *CE = dyn_cast<const CXXMemberCallExpr *>(E);
   auto *D = dyn_cast<const CXXDeleteExpr *>(E);
   assert((CE != nullptr) ^ (D != nullptr));
   assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
-  assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
+  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);
+  GlobalDecl GD(Dtor, Dtor_VectorDeleting);
   const CGFunctionInfo *FInfo =
       &CGM.getTypes().arrangeCXXStructorDeclaration(GD);
   llvm::FunctionType *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
@@ -2013,7 +2025,7 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
   ASTContext &Context = getContext();
   llvm::Value *ImplicitParam = llvm::ConstantInt::get(
       llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
-      DtorType == Dtor_Deleting);
+      2 * (ArrayDeletion) + (DtorType == Dtor_Deleting));
 
   QualType ThisTy;
   if (CE) {
@@ -4053,6 +4065,19 @@ 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 objects with the correct type for the vector and scalar
+    // deleting destructors.
+    GlobalDecl VectorDtorGD(dtor, Dtor_VectorDeleting);
+    GlobalDecl ScalarDtorGD(dtor, Dtor_Deleting);
+
+    // Emit an alias from the vector deleting destructor to the scalar deleting
+    // destructor.
+    CGM.EmitDefinitionAsAlias(VectorDtorGD, ScalarDtorGD);
+    return;
+  }
+
   llvm::Function *Fn = CGM.codegenCXXStructor(GD);
   if (Fn->isWeakForLinker())
     Fn->setComdat(CGM.getModule().getOrInsertComdat(Fn->getName()));
diff --git a/clang/test/CodeGenCXX/debug-info-windows-dtor.cpp b/clang/test/CodeGenCXX/debug-info-windows-dtor.cpp
index beea56ce7368b..ffef45b9f7d1b 100644
--- a/clang/test/CodeGenCXX/debug-info-windows-dtor.cpp
+++ b/clang/test/CodeGenCXX/debug-info-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/CodeGenCXX/dllexport.cpp b/clang/test/CodeGenCXX/dllexport.cpp
index c8ac526f4cbe3..16eaac75e702f 100644
--- a/clang/test/CodeGenCXX/dllexport.cpp
+++ b/clang/test/CodeGenCXX/dllexport.cpp
@@ -631,7 +631,7 @@ 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"
+// 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 07abc3d065e5e..2ff7391ec8c8f 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp
@@ -52,7 +52,8 @@ struct C {
 // DTORS:        store ptr %{{.*}}, ptr %[[RETVAL:retval]]
 // DTORS:        %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32, ptr %[[SHOULD_DELETE_VAR]]
 // DTORS:        call x86_thiscallcc void @"??1C at basic@@UAE at XZ"(ptr {{[^,]*}} %[[THIS:[0-9a-z]+]])
-// DTORS-NEXT:   %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0
+// DTORS-NEXT:   %[[AND:[0-9]+]] = and i32 %[[SHOULD_DELETE_VALUE]], 1
+// DTORS-NEXT:   %[[CONDITION:[0-9]+]] = icmp eq i32 %[[AND]], 0
 // DTORS-NEXT:   br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]]
 //
 // DTORS:      [[CALL_DELETE_LABEL]]
@@ -166,7 +167,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..4eefc46824467
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-vector-deleting-dtors.cpp
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -emit-llvm %s -triple=x86_64-pc-windows-msvc -o - | FileCheck %s
+
+struct Bird {
+  virtual ~Bird();
+};
+
+struct Parrot : public Bird {
+// CHECK: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_EParrot@@UEAAPEAXI at Z"] }, comdat($"??_7Parrot@@6B@")
+// CHECK: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_EBird@@UEAAPEAXI at Z"] }, comdat($"??_7Bird@@6B@")
+  virtual ~Parrot() {}
+};
+
+Bird::~Bird() {}
+
+// Vector deleting dtor for Bird is an alias because no new Bird[] expressions
+// in the TU.
+// CHECK: @"??_EBird@@UEAAPEAXI at Z" = weak dso_local unnamed_addr alias ptr (ptr, i32), ptr @"??_GBird@@UEAAPEAXI at Z"
+// No scalar destructor for Parrot.
+// CHECK-NOT: @"??_GParrot"
+// No vector destructor definition for Bird.
+// CHECK-NOT: define{{.*}}@"??_EBird"
+
+void dealloc(Bird *p) {
+  delete[] p;
+}
+
+Bird* alloc() {
+  Parrot* P = new Parrot[38];
+  return P;
+}
+
+void bar() {
+  dealloc(alloc());
+}
+
+// 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:
+// CHECK-NEXT:   %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LPTR]], i64 -8
+// CHECK-NEXT:   %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
+// CHECK-NEXT:   %[[ISNOELEM:.*]] = icmp eq i64 %2, 0
+// CHECK-NEXT:   br i1 %[[ISNOELEM]], label %vdtor.nocall, label %vdtor.call
+// CHECK: vdtor.nocall:
+// CHECK-NEXT:   %[[HOWMANYBYTES:.*]] = mul i64 8, %[[HOWMANY]]
+// CHECK-NEXT:   %[[ADDCOOKIESIZE:.*]] = add i64 %[[HOWMANYBYTES]], 8
+// CHECK-NEXT:   call void @"??_V at YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef %[[ADDCOOKIESIZE]])
+// CHECK-NEXT:   br label %delete.end
+// CHECK: vdtor.call:
+// CHECK-NEXT:   %[[VTABLE:.*]] = load ptr, ptr %[[LPTR]], align 8
+// CHECK-NEXT:   %[[FPGEP:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0
+// CHECK-NEXT:   %[[FPLOAD:.*]]  = load ptr, ptr %[[FPGEP]], align 8
+// CHECK-NEXT:   %[[CALL:.*]] = call noundef ptr %[[FPLOAD]](ptr noundef nonnull align 8 dereferenceable(8) %[[LPTR]], i32 noundef 3)
+// CHECK-NEXT:   br label %delete.end
+// CHECK: delete.end:
+// CHECK-NEXT:   ret void
+
+// Vector dtor definition for Parrot.
+// CHECK-LABEL: define weak dso_local noundef ptr @"??_EParrot@@UEAAPEAXI at Z"(
+// CHECK-SAME: ptr {{.*}} %[[THIS:.*]], i32 {{.*}} %[[IMPLICIT_PARAM:.*]])
+// 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:
+// CHECK-NEXT:   %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LTHIS]], i64 -8
+// CHECK-NEXT:   %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
+// CHECK-NEXT:   %[[END:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[LTHIS]], i64 %[[HOWMANY]]
+// CHECK-NEXT:   br label %arraydestroy.body
+// CHECK: arraydestroy.body:
+// CHECK-NEXT:   %[[PASTELEM:.*]] = phi ptr [ %delete.end, %dtor.vector ], [ %arraydestroy.element, %arraydestroy.body ]
+// CHECK-NEXT:   %[[CURELEM:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[PASTELEM]], i64 -1
+// CHECK-NEXT:   call void @"??1Parrot@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[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:
+// CHECK-NEXT:   call void @"??3 at YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef 8)
+// CHECK-NEXT:   br label %dtor.continue
+// CHECK: dtor.scalar:
+// CHECK-NEXT:   call void @"??1Parrot@@UEAA at XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[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:
+// CHECK-NEXT:   call void @"??3 at YAXPEAX_K@Z"(ptr noundef %[[LTHIS]], i64 noundef 8) #3
+// CHECK-NEXT:   br label %dtor.continue
+// CHECK: dtor.continue:
+// CHECK-NEXT:   %[[LOADRET:.*]] = load ptr, ptr %[[RET]], align 8
+// CHECK-NEXT:   ret ptr %[[LOADRET]]
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/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/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"({{.*}})

>From 42cf015f816e4b3807484e06a5eaa004b922ce4e Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Fri, 7 Feb 2025 04:57:52 -0800
Subject: [PATCH 2/5] clang-format related code

---
 clang/lib/AST/MicrosoftMangle.cpp     | 12 +++++++-----
 clang/lib/CodeGen/CGClass.cpp         |  5 +++--
 clang/lib/CodeGen/CGExprCXX.cpp       |  8 +++-----
 clang/lib/CodeGen/ItaniumCXXABI.cpp   | 10 ++++------
 clang/lib/CodeGen/MicrosoftCXXABI.cpp |  4 +---
 5 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index d5dc7833a7fcc..6e2be2557d0a7 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1484,7 +1484,9 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
   // <operator-name> ::= ?_G # scalar deleting destructor
   case Dtor_Deleting: Out << "?_G"; return;
   // <operator-name> ::= ?_E # vector deleting destructor
-  case Dtor_VectorDeleting: Out << "?_E"; return;
+  case Dtor_VectorDeleting:
+    Out << "?_E";
+    return;
   case Dtor_Comdat:
     llvm_unreachable("not expecting a COMDAT");
   }
@@ -2885,10 +2887,10 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
   //               ::= @ # structors (they have no declared return type)
   if (IsStructor) {
     if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
-      // 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.
+      // 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");
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index bb94d502c34fa..837f4186da5d6 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1440,7 +1440,7 @@ llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
     return CGF.EmitScalarExpr(ThisArg);
   return CGF.LoadCXXThis();
 }
-}
+} // namespace
 
 void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
                                   CodeGenFunction &CGF,
@@ -1570,7 +1570,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
   switch (DtorType) {
   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_VectorDeleting:
+    llvm_unreachable("already handled vector deleting case");
 
   case Dtor_Complete:
     assert((Body || getTarget().getCXXABI().isMicrosoft()) &&
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 5b00f79b03293..998d319c58c02 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1914,10 +1914,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,
                              bool ArrayDeletion) {
   // C++11 [expr.delete]p3:
@@ -2164,7 +2162,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
 
         EmitBlock(bodyBB);
         if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd,
-                              /*ArrayDeletion*/true))
+                              /*ArrayDeletion*/ true))
           EmitBlock(DeleteEnd);
         return;
       }
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index ea1ec6cbc1e72..882090e3ea0d2 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -182,9 +182,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
   }
 
   bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
-  bool hasVectorDeletingDtors() override {
-    return false;
-  }
+  bool hasVectorDeletingDtors() override { return false; }
   void EmitBadTypeidCall(CodeGenFunction &CGF) override;
   llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
                           Address ThisPtr,
@@ -455,7 +453,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
        if (!IsInlined)
          continue;
 
-       StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl(false));
+       StringRef Name =
+           CGM.getMangledName(VtableComponent.getGlobalDecl(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
@@ -1373,8 +1372,7 @@ bool ItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
 /// at entry -2 in the vtable.
 void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
                                             const CXXDeleteExpr *DE,
-                                            Address Ptr,
-                                            QualType ElementType,
+                                            Address Ptr, QualType ElementType,
                                             const CXXDestructorDecl *Dtor,
                                             bool ArrayDeletion) {
   bool UseGlobalDelete = DE->isGlobalDelete();
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index daeb19071df36..ed259ce2d38e6 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -146,9 +146,7 @@ class MicrosoftCXXABI : public CGCXXABI {
   }
 
   bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
-  bool hasVectorDeletingDtors() override {
-    return true;
-  }
+  bool hasVectorDeletingDtors() override { return true; }
   void EmitBadTypeidCall(CodeGenFunction &CGF) override;
   llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
                           Address ThisPtr,

>From 5f874022c126d1ecfc50903b6a34634058c79aa6 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 20 Feb 2025 05:27:42 -0800
Subject: [PATCH 3/5] First round of cleanup after review feedback

---
 clang/lib/CodeGen/CGCXXABI.h          | 16 +++++------
 clang/lib/CodeGen/CGClass.cpp         | 33 ++++++++--------------
 clang/lib/CodeGen/CGExprCXX.cpp       | 18 +++++-------
 clang/lib/CodeGen/ItaniumCXXABI.cpp   | 23 +++++++--------
 clang/lib/CodeGen/MicrosoftCXXABI.cpp | 40 ++++++++++++---------------
 5 files changed, 54 insertions(+), 76 deletions(-)

diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index fbbe8f6c6142c..d27ea48929b40 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -251,10 +251,9 @@ class CGCXXABI {
 
 public:
   virtual void emitVirtualObjectDelete(CodeGenFunction &CGF,
-                                       const CXXDeleteExpr *DE, Address Ptr,
-                                       QualType ElementType,
-                                       const CXXDestructorDecl *Dtor,
-                                       bool ArrayDeletion) = 0;
+                                       const CXXDeleteExpr *DE,
+                                       Address Ptr, QualType ElementType,
+                                       const CXXDestructorDecl *Dtor) = 0;
   virtual void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) = 0;
   virtual void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) = 0;
   virtual llvm::GlobalVariable *getThrowInfo(QualType T) { return nullptr; }
@@ -487,10 +486,11 @@ class CGCXXABI {
       llvm::PointerUnion<const CXXDeleteExpr *, const CXXMemberCallExpr *>;
 
   /// Emit the ABI-specific virtual destructor call.
-  virtual llvm::Value *EmitVirtualDestructorCall(
-      CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
-      Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
-      bool ArrayDeletion = false) = 0;
+  virtual llvm::Value *
+  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
+                            CXXDtorType DtorType, Address This,
+                            DeleteOrMemberCallExpr E,
+                            llvm::CallBase **CallOrInvoke) = 0;
 
   virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
                                                 GlobalDecl GD,
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 837f4186da5d6..91f232e082804 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1433,18 +1433,9 @@ static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
   return true;
 }
 
-namespace {
-llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
-                                   const CXXDestructorDecl *DD) {
-  if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
-    return CGF.EmitScalarExpr(ThisArg);
-  return CGF.LoadCXXThis();
-}
-} // namespace
-
-void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
-                                  CodeGenFunction &CGF,
-                                  llvm::Value *ShouldDeleteCondition) {
+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 =
@@ -1452,9 +1443,7 @@ void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
   llvm::BasicBlock *VectorBB = CGF.createBasicBlock("dtor.vector");
   auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
   llvm::Value *CheckTheBitForArrayDestroy = CGF.Builder.CreateAnd(
-      ShouldDeleteCondition,
-      llvm::Constant::getIntegerValue(CondTy, llvm::APInt(CondTy->getBitWidth(),
-                                                          /*val=*/2)));
+      ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 2));
   llvm::Value *ShouldDestroyArray =
       CGF.Builder.CreateIsNull(CheckTheBitForArrayDestroy);
   CGF.Builder.CreateCondBr(ShouldDestroyArray, ScalarBB, VectorBB);
@@ -1492,9 +1481,7 @@ void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
   CGF.EmitBlock(VectorBBCont);
 
   llvm::Value *CheckTheBitForDeleteCall = CGF.Builder.CreateAnd(
-      ShouldDeleteCondition,
-      llvm::Constant::getIntegerValue(CondTy, llvm::APInt(CondTy->getBitWidth(),
-                                                          /*val=*/1)));
+      ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
 
   llvm::Value *ShouldCallDelete =
       CGF.Builder.CreateIsNull(CheckTheBitForDeleteCall);
@@ -1648,6 +1635,12 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args)
 }
 
 namespace {
+  llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
+                                     const CXXDestructorDecl *DD) {
+    if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
+      return CGF.EmitScalarExpr(ThisArg);
+    return CGF.LoadCXXThis();
+  }
   /// Call the operator delete associated with the current destructor.
   struct CallDtorDelete final : EHScopeStack::Cleanup {
     CallDtorDelete() {}
@@ -1668,9 +1661,7 @@ namespace {
     llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
     auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
     llvm::Value *CheckTheBit = CGF.Builder.CreateAnd(
-        ShouldDeleteCondition, llvm::Constant::getIntegerValue(
-                                   CondTy, llvm::APInt(CondTy->getBitWidth(),
-                                                       /*val=*/1)));
+        ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
     llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(CheckTheBit);
     CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
 
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 998d319c58c02..d4e14f4574b87 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1904,8 +1904,8 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
                                        QualType ElementType) {
   auto *Dtor = ElementType->getAsCXXRecordDecl()->getDestructor();
   if (Dtor && Dtor->isVirtual())
-    CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType, Dtor,
-                                                /*ArrayDeletion=*/false);
+    CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
+                                                Dtor);
   else
     CGF.EmitDeleteCall(DE->getOperatorDelete(), Ptr.emitRawPointer(CGF),
                        ElementType);
@@ -1916,8 +1916,7 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
 /// if not.
 static bool EmitObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
                              Address Ptr, QualType ElementType,
-                             llvm::BasicBlock *UnconditionalDeleteBlock,
-                             bool ArrayDeletion) {
+                             llvm::BasicBlock *UnconditionalDeleteBlock) {
   // C++11 [expr.delete]p3:
   //   If the static type of the object to be deleted is different from its
   //   dynamic type, the static type shall be a base class of the dynamic type
@@ -1962,7 +1961,7 @@ static bool EmitObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
         }
         if (UseVirtualCall) {
           CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
-                                                      Dtor, ArrayDeletion);
+                                                      Dtor);
           return false;
         }
       }
@@ -2148,9 +2147,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
 
         auto *CondTy = cast<llvm::IntegerType>(NumElements->getType());
         llvm::Value *isEmpty = Builder.CreateICmpEQ(
-            NumElements, llvm::Constant::getIntegerValue(
-                             CondTy, llvm::APInt(CondTy->getBitWidth(),
-                                                 /*val=*/0)));
+            NumElements, llvm::ConstantInt::get(CondTy, 0));
         Builder.CreateCondBr(isEmpty, doneBB, bodyBB);
 
         // Delete cookie for empty array.
@@ -2161,8 +2158,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
         EmitBranch(DeleteEnd);
 
         EmitBlock(bodyBB);
-        if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd,
-                              /*ArrayDeletion*/ true))
+        if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
           EmitBlock(DeleteEnd);
         return;
       }
@@ -2173,7 +2169,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
     EmitArrayDelete(*this, E, Ptr, DeleteTy);
     EmitBlock(DeleteEnd);
   } else {
-    if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd, E->isArrayForm()))
+    if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
       EmitBlock(DeleteEnd);
   }
 }
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 95051754ad9bd..e60281d7d607e 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -161,8 +161,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
 
   void emitVirtualObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
                                Address Ptr, QualType ElementType,
-                               const CXXDestructorDecl *Dtor,
-                               bool ArrayDeletion = false) override;
+                               const CXXDestructorDecl *Dtor) override;
 
   void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override;
   void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;
@@ -318,12 +317,11 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
                                      Address This, llvm::Type *Ty,
                                      SourceLocation Loc) override;
 
-  llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
-                                         const CXXDestructorDecl *Dtor,
-                                         CXXDtorType DtorType, Address This,
-                                         DeleteOrMemberCallExpr E,
-                                         llvm::CallBase **CallOrInvoke,
-                                         bool ArrayDeletion = false) override;
+  llvm::Value *
+  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
+                            CXXDtorType DtorType, Address This,
+                            DeleteOrMemberCallExpr E,
+                            llvm::CallBase **CallOrInvoke) override;
 
   void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
 
@@ -1374,9 +1372,9 @@ bool ItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
 /// at entry -2 in the vtable.
 void ItaniumCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
                                             const CXXDeleteExpr *DE,
-                                            Address Ptr, QualType ElementType,
-                                            const CXXDestructorDecl *Dtor,
-                                            bool ArrayDeletion) {
+                                            Address Ptr,
+                                            QualType ElementType,
+                                            const CXXDestructorDecl *Dtor) {
   bool UseGlobalDelete = DE->isGlobalDelete();
   if (UseGlobalDelete) {
     // Derive the complete-object pointer, which is what we need
@@ -2246,8 +2244,7 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
 
 llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
     CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
-    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
-    bool ArrayDeletion) {
+    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke) {
   auto *CE = dyn_cast<const CXXMemberCallExpr *>(E);
   auto *D = dyn_cast<const CXXDeleteExpr *>(E);
   assert((CE != nullptr) ^ (D != nullptr));
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 8238c9de39c53..2ffd989759d15 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -120,8 +120,7 @@ class MicrosoftCXXABI : public CGCXXABI {
 
   void emitVirtualObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
                                Address Ptr, QualType ElementType,
-                               const CXXDestructorDecl *Dtor,
-                               bool ArrayDeletion) override;
+                               const CXXDestructorDecl *Dtor) override;
 
   void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override;
   void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;
@@ -336,12 +335,11 @@ class MicrosoftCXXABI : public CGCXXABI {
                                      Address This, llvm::Type *Ty,
                                      SourceLocation Loc) override;
 
-  llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
-                                         const CXXDestructorDecl *Dtor,
-                                         CXXDtorType DtorType, Address This,
-                                         DeleteOrMemberCallExpr E,
-                                         llvm::CallBase **CallOrInvoke,
-                                         bool ArrayDeletion) override;
+  llvm::Value *
+  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
+                            CXXDtorType DtorType, Address This,
+                            DeleteOrMemberCallExpr E,
+                            llvm::CallBase **CallOrInvoke) override;
 
   void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
                                         CallArgList &CallArgs) override {
@@ -891,16 +889,15 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
 
 void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF,
                                               const CXXDeleteExpr *DE,
-                                              Address Ptr, QualType ElementType,
-                                              const CXXDestructorDecl *Dtor,
-                                              bool ArrayDeletion) {
+                                              Address Ptr,
+                                              QualType ElementType,
+                                              const CXXDestructorDecl *Dtor) {
   // FIXME: Provide a source location here even though there's no
   // CXXMemberCallExpr for dtor call.
   bool UseGlobalDelete = DE->isGlobalDelete();
   CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
-  llvm::Value *MDThis =
-      EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
-                                /*CallOrInvoke=*/nullptr, ArrayDeletion);
+  llvm::Value *MDThis = EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE,
+                                                  /*CallOrInvoke=*/nullptr);
   if (UseGlobalDelete)
     CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType);
 }
@@ -2005,8 +2002,7 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
 
 llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
     CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
-    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke,
-    bool ArrayDeletion) {
+    Address This, DeleteOrMemberCallExpr E, llvm::CallBase **CallOrInvoke) {
   auto *CE = dyn_cast<const CXXMemberCallExpr *>(E);
   auto *D = dyn_cast<const CXXDeleteExpr *>(E);
   assert((CE != nullptr) ^ (D != nullptr));
@@ -2023,9 +2019,8 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
   CGCallee Callee = CGCallee::forVirtual(CE, GD, This, Ty);
 
   ASTContext &Context = getContext();
-  llvm::Value *ImplicitParam = llvm::ConstantInt::get(
-      llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
-      2 * (ArrayDeletion) + (DtorType == Dtor_Deleting));
+  uint32_t Flags = ((D && D->isArrayForm()) << 1) | (DtorType == Dtor_Deleting);
+  llvm::Value *ImplicitParam = CGF.Builder.getInt32(Flags);
 
   QualType ThisTy;
   if (CE) {
@@ -4067,14 +4062,13 @@ void MicrosoftCXXABI::emitCXXStructor(GlobalDecl GD) {
 
   if (GD.getDtorType() == Dtor_VectorDeleting &&
       !CGM.classNeedsVectorDestructor(dtor->getParent())) {
-    // Create GlobalDecl objects with the correct type for the vector and scalar
-    // deleting destructors.
-    GlobalDecl VectorDtorGD(dtor, Dtor_VectorDeleting);
+    // 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(VectorDtorGD, ScalarDtorGD);
+    CGM.EmitDefinitionAsAlias(GD, ScalarDtorGD);
     return;
   }
 

>From 736ccf9188114fb78d9bc8112bc0fffb1895b0a3 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 20 Feb 2025 08:54:15 -0800
Subject: [PATCH 4/5] Try to avoid unnamed bool parameter

---
 clang/lib/CodeGen/ItaniumCXXABI.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index e60281d7d607e..2d92002f08544 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -451,8 +451,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
        if (!IsInlined)
          continue;
 
-       StringRef Name =
-           CGM.getMangledName(VtableComponent.getGlobalDecl(false));
+       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

>From a26d7b8e454bc0abda4fe02ca8ea89323dc4b118 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 20 Feb 2025 10:45:10 -0800
Subject: [PATCH 5/5] Add comment to weak linkage

---
 clang/lib/CodeGen/MicrosoftCXXABI.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 2ffd989759d15..4b55fc3f17bd7 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1413,6 +1413,10 @@ llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
     // is internal.
     return llvm::GlobalValue::LinkOnceODRLinkage;
   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");



More information about the cfe-commits mailing list