[clang] WIP: [clang] MicrosoftCXXABI: Serialize the exception copy constructor table in the AST (PR #114075)

Andrey Glebov via cfe-commits cfe-commits at lists.llvm.org
Sat May 31 04:30:36 PDT 2025


https://github.com/glebov-andrey updated https://github.com/llvm/llvm-project/pull/114075

>From 4698bff9789ee73a2161f49ba509b0ce705ec49f Mon Sep 17 00:00:00 2001
From: Andrey Glebov <andrey458641387 at gmail.com>
Date: Mon, 28 Oct 2024 00:02:51 +0300
Subject: [PATCH] [clang] MicrosoftCXXABI: restore the RecordToCopyCtor table
 when loading AST (#53486)

- Includes a regression test for the issue
---
 clang/include/clang/AST/ASTContext.h          |  5 ++++
 clang/include/clang/AST/ExternalASTSource.h   |  4 +++
 .../clang/Sema/MultiplexExternalSemaSource.h  |  4 +++
 .../include/clang/Serialization/ASTBitCodes.h |  2 ++
 clang/include/clang/Serialization/ASTReader.h | 10 +++++++
 clang/lib/AST/ASTContext.cpp                  | 17 +++++++++++
 clang/lib/AST/CXXABI.h                        |  3 ++
 clang/lib/AST/ExternalASTSource.cpp           |  3 ++
 clang/lib/AST/ItaniumCXXABI.cpp               |  5 ++++
 clang/lib/AST/MicrosoftCXXABI.cpp             |  5 ++++
 .../lib/Sema/MultiplexExternalSemaSource.cpp  |  6 ++++
 clang/lib/Serialization/ASTReader.cpp         | 20 +++++++++++++
 clang/lib/Serialization/ASTWriter.cpp         | 14 +++++++++
 .../CodeGenCXX/microsoft-abi-throw-pch.cpp    | 13 ++++++++
 .../test/CodeGenCXX/microsoft-abi-throw-pch.h | 30 +++++++++++++++++++
 15 files changed, 141 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/microsoft-abi-throw-pch.cpp
 create mode 100644 clang/test/CodeGenCXX/microsoft-abi-throw-pch.h

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 8d24d393eab09..84633d0d84e76 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -682,6 +682,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// For performance, track whether any function effects are in use.
   mutable bool AnyFunctionEffects = false;
 
+  bool ExternalCopyConstructorsForExceptionObjectsLoaded = false;
+
   const TargetInfo *Target = nullptr;
   const TargetInfo *AuxTarget = nullptr;
   clang::PrintingPolicy PrintingPolicy;
@@ -3362,6 +3364,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
       const FunctionDecl *FD,
       llvm::function_ref<void(FunctionDecl *)> Pred) const;
 
+  llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> *
+  getRecordToCopyCtor();
+
   const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *RD);
 
diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h
index f45e3af7602c1..4eba9dac5ce07 100644
--- a/clang/include/clang/AST/ExternalASTSource.h
+++ b/clang/include/clang/AST/ExternalASTSource.h
@@ -40,6 +40,7 @@ class ASTConsumer;
 class ASTContext;
 class ASTSourceDescriptor;
 class CXXBaseSpecifier;
+class CXXConstructorDecl;
 class CXXCtorInitializer;
 class CXXRecordDecl;
 class DeclarationName;
@@ -175,6 +176,9 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs);
 
+  virtual void LoadExternalExceptionCopyingConstructors(
+      llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &RecordToCtor);
+
   /// Ensures that the table of all visible declarations inside this
   /// context is up to date.
   ///
diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
index 391c2177d75ec..17962c79a930e 100644
--- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h
+++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
@@ -106,6 +106,10 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs) override;
 
+  void LoadExternalExceptionCopyingConstructors(
+      llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &RecordToCtor)
+      override;
+
   /// Ensures that the table of all visible declarations inside this
   /// context is up to date.
   void completeVisibleDeclsMap(const DeclContext *DC) override;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 9d265f27b8e31..fc68f484741e5 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -742,6 +742,8 @@ enum ASTRecordTypes {
   UPDATE_MODULE_LOCAL_VISIBLE = 76,
 
   UPDATE_TU_LOCAL_VISIBLE = 77,
+
+  MSCXXABI_EXCEPTION_COPYING_CONSTRUCTORS = 78,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 6963611c6a815..212898a77dfe7 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -1055,6 +1055,12 @@ class ASTReader
   /// The IDs of all decls with function effects to be checked.
   SmallVector<GlobalDeclID> DeclsWithEffectsToVerify;
 
+  struct RecordAndCopyingCtor {
+    GlobalDeclID RecordID;
+    GlobalDeclID CtorID;
+  };
+  SmallVector<RecordAndCopyingCtor> RecordToCopyingCtor;
+
 private:
   struct ImportedSubmodule {
     serialization::SubmoduleID ID;
@@ -2177,6 +2183,10 @@ class ASTReader
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs) override;
 
+  void LoadExternalExceptionCopyingConstructors(
+      llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &RecordToCtor)
+      override;
+
   /// Finds all the visible declarations with a given name.
   /// The current implementation of this method just loads the entire
   /// lookup table as unmaterialized references.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5044d7c33ec3c..aee63455b538c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13333,8 +13333,25 @@ ASTContext::createMangleNumberingContext() const {
   return ABI->createMangleNumberingContext();
 }
 
+llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> *
+ASTContext::getRecordToCopyCtor() {
+  if (ABI) { // TODO Why can this be null?
+    return ABI->getRecordToCopyCtor();
+  }
+  return nullptr;
+}
+
 const CXXConstructorDecl *
 ASTContext::getCopyConstructorForExceptionObject(CXXRecordDecl *RD) {
+  if (!getTargetInfo().getCXXABI().isMicrosoft()) {
+    return nullptr;
+  }
+  if (ExternalSource && !ExternalCopyConstructorsForExceptionObjectsLoaded) {
+    auto *Map = ABI->getRecordToCopyCtor();
+    assert(Map);
+    ExternalSource->LoadExternalExceptionCopyingConstructors(*Map);
+    ExternalCopyConstructorsForExceptionObjectsLoaded = true;
+  }
   return ABI->getCopyConstructorForExceptionObject(
       cast<CXXRecordDecl>(RD->getFirstDecl()));
 }
diff --git a/clang/lib/AST/CXXABI.h b/clang/lib/AST/CXXABI.h
index 9258a53fefebc..612c9779346bd 100644
--- a/clang/lib/AST/CXXABI.h
+++ b/clang/lib/AST/CXXABI.h
@@ -56,6 +56,9 @@ class CXXABI {
   virtual void addCopyConstructorForExceptionObject(CXXRecordDecl *,
                                                     CXXConstructorDecl *) = 0;
 
+  virtual llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> *
+  getRecordToCopyCtor() = 0;
+
   /// Retrieves the mapping from class to copy constructor for this C++ ABI.
   virtual const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *) = 0;
diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp
index e8c1004089713..060aa5f7ac474 100644
--- a/clang/lib/AST/ExternalASTSource.cpp
+++ b/clang/lib/AST/ExternalASTSource.cpp
@@ -109,6 +109,9 @@ bool ExternalASTSource::LoadExternalSpecializations(
   return false;
 }
 
+void ExternalASTSource::LoadExternalExceptionCopyingConstructors(
+    llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &) {}
+
 void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {}
 
 void ExternalASTSource::FindExternalLexicalDecls(
diff --git a/clang/lib/AST/ItaniumCXXABI.cpp b/clang/lib/AST/ItaniumCXXABI.cpp
index 6ceedd657fe7e..f85828148c763 100644
--- a/clang/lib/AST/ItaniumCXXABI.cpp
+++ b/clang/lib/AST/ItaniumCXXABI.cpp
@@ -256,6 +256,11 @@ class ItaniumCXXABI : public CXXABI {
     return Layout.getNonVirtualSize() == PointerSize;
   }
 
+  llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> *
+  getRecordToCopyCtor() override {
+    return nullptr;
+  }
+
   const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *RD) override {
     return nullptr;
diff --git a/clang/lib/AST/MicrosoftCXXABI.cpp b/clang/lib/AST/MicrosoftCXXABI.cpp
index 1c020c3ad4ad5..472ca10054b4c 100644
--- a/clang/lib/AST/MicrosoftCXXABI.cpp
+++ b/clang/lib/AST/MicrosoftCXXABI.cpp
@@ -146,6 +146,11 @@ class MicrosoftCXXABI : public CXXABI {
     llvm_unreachable("unapplicable to the MS ABI");
   }
 
+  llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> *
+  getRecordToCopyCtor() override {
+    return &RecordToCopyCtor;
+  }
+
   const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *RD) override {
     return RecordToCopyCtor[RD];
diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
index fbfb242598c24..46ba2f63a87b1 100644
--- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp
+++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
@@ -142,6 +142,12 @@ bool MultiplexExternalSemaSource::LoadExternalSpecializations(
   return AnyNewSpecsLoaded;
 }
 
+void MultiplexExternalSemaSource::LoadExternalExceptionCopyingConstructors(
+    llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &RecordToCtor) {
+  for (size_t i = 0; i < Sources.size(); ++i)
+    Sources[i]->LoadExternalExceptionCopyingConstructors(RecordToCtor);
+}
+
 void MultiplexExternalSemaSource::completeVisibleDeclsMap(const DeclContext *DC){
   for(size_t i = 0; i < Sources.size(); ++i)
     Sources[i]->completeVisibleDeclsMap(DC);
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 8d2f105b2dec3..df5efa3a9e28e 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -4358,6 +4358,16 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       for (unsigned I = 0, N = Record.size(); I != N; /*in loop*/)
         DeclsToCheckForDeferredDiags.insert(ReadDeclID(F, Record, I));
       break;
+
+    case MSCXXABI_EXCEPTION_COPYING_CONSTRUCTORS:
+      if (Record.size() % 2 != 0)
+        return llvm::createStringError(
+            std::errc::illegal_byte_sequence,
+            "Invalid MSCXXABI_EXCEPTION_COPYING_CONSTRUCTORS record");
+      for (unsigned I = 0, N = Record.size(); I < N; /* in loop */)
+        RecordToCopyingCtor.push_back(
+            {ReadDeclID(F, Record, I), ReadDeclID(F, Record, I)});
+      break;
     }
   }
 }
@@ -8418,6 +8428,16 @@ bool ASTReader::LoadExternalSpecializations(
   return NewDeclsFound;
 }
 
+void ASTReader::LoadExternalExceptionCopyingConstructors(
+    llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> &RecordToCtor) {
+  for (const auto &[RecordID, CtorID] : RecordToCopyingCtor) {
+    auto *RD = cast<CXXRecordDecl>(GetDecl(RecordID));
+    auto *CD = cast<CXXConstructorDecl>(GetDecl(CtorID));
+    RecordToCtor.insert({RD, CD});
+  }
+  RecordToCopyingCtor.clear();
+}
+
 void ASTReader::FindExternalLexicalDecls(
     const DeclContext *DC, llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
     SmallVectorImpl<Decl *> &Decls) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index cc9916a75d4b4..d493dc7453105 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5857,6 +5857,20 @@ void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
 
   if (!VTablesToEmit.empty())
     Stream.EmitRecord(VTABLES_TO_EMIT, VTablesToEmit);
+
+  if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
+    const auto *RecordToCopyCtor = Context.getRecordToCopyCtor();
+    if (RecordToCopyCtor) {
+      RecordData ExceptionCopyingConstructors;
+      for (const auto &[RD, CD] : *RecordToCopyCtor) {
+        AddDeclRef(RD, ExceptionCopyingConstructors);
+        AddDeclRef(CD, ExceptionCopyingConstructors);
+      }
+      if (!ExceptionCopyingConstructors.empty())
+        Stream.EmitRecord(MSCXXABI_EXCEPTION_COPYING_CONSTRUCTORS,
+                          ExceptionCopyingConstructors);
+    }
+  }
 }
 
 ASTFileSignature ASTWriter::WriteASTCore(Sema *SemaPtr, StringRef isysroot,
diff --git a/clang/test/CodeGenCXX/microsoft-abi-throw-pch.cpp b/clang/test/CodeGenCXX/microsoft-abi-throw-pch.cpp
new file mode 100644
index 0000000000000..6b3570ba272bc
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-throw-pch.cpp
@@ -0,0 +1,13 @@
+// Regression test for https://github.com/llvm/llvm-project/issues/53486
+
+// RUN: %clang_cc1 -x c++ -std=c++11 -fcxx-exceptions -fexceptions -triple=x86_64-pc-windows-msvc -emit-pch -building-pch-with-obj -fmodules-codegen -o %t.pch %S/microsoft-abi-throw-pch.h
+// RUN: %clang_cc1 -x c++ -std=c++11 -fcxx-exceptions -fexceptions -triple=x86_64-pc-windows-msvc -include-pch %t.pch -emit-llvm -building-pch-with-obj -fmodules-codegen -o - %s | FileCheck %s
+
+// CHECK-DAG: @"_CT??_R0?AUTrivial@@@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"??_R0?AUTrivial@@@8" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 1, i32 0 }, section ".xdata", comdat
+// CHECK-DAG: @"_CTA1?AUTrivial@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"_CT??_R0?AUTrivial@@@81" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
+
+// CHECK-DAG: @"_CT??_R0?AUNonTrivial@@@8??0NonTrivial@@QEAA at AEBU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"??_R0?AUNonTrivial@@@8" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"??0NonTrivial@@QEAA at AEBU0@@Z" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32) }, section ".xdata", comdat
+// CHECK-DAG: @"_CTA1?AUNonTrivial@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"_CT??_R0?AUNonTrivial@@@8??0NonTrivial@@QEAA at AEBU0@@Z1" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
+
+// CHECK-DAG: @"_CT??_R0?AUTemplateWithDefault@@@8??$?_OH at TemplateWithDefault@@QEAAXAEAU0@@Z1" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"??_R0?AUTemplateWithDefault@@@8" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"??$?_OH at TemplateWithDefault@@QEAAXAEAU0@@Z" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32) }, section ".xdata", comdat
+// CHECK-DAG: @"_CTA1?AUTemplateWithDefault@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (ptr @"_CT??_R0?AUTemplateWithDefault@@@8??$?_OH at TemplateWithDefault@@QEAAXAEAU0@@Z1" to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
diff --git a/clang/test/CodeGenCXX/microsoft-abi-throw-pch.h b/clang/test/CodeGenCXX/microsoft-abi-throw-pch.h
new file mode 100644
index 0000000000000..160f2f01370f0
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-throw-pch.h
@@ -0,0 +1,30 @@
+// Header for PCH test microsoft-abi-throw-pch.cpp
+
+struct Trivial {};
+
+struct NonTrivial {
+  NonTrivial() = default;
+  NonTrivial(const NonTrivial &) noexcept {}
+  NonTrivial(NonTrivial &&) noexcept {}
+};
+
+struct TemplateWithDefault {
+  template <typename T>
+  static int f() {
+    return 0;
+  }
+  template <typename T = int>
+  TemplateWithDefault(TemplateWithDefault &, T = f<T>());
+};
+
+inline void throw_trivial() {
+  throw Trivial();
+}
+
+inline void throw_non_trivial() {
+  throw NonTrivial();
+}
+
+inline void throw_template(TemplateWithDefault &e) {
+  throw e;
+}



More information about the cfe-commits mailing list