[llvm-branch-commits] [clang] [Serialization] Code cleanups and polish 83233 (PR #83237)

Chuanqi Xu via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Nov 8 01:43:53 PST 2024


https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/83237

>From 493cb7cdafeb22efa9e9f3c1954b6de1e2665365 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Wed, 28 Feb 2024 11:41:53 +0800
Subject: [PATCH 1/2] [Serialization] Code cleanups and polish 83233

---
 clang/include/clang/AST/DeclTemplate.h        |  32 +-
 clang/include/clang/AST/ExternalASTSource.h   |   8 +-
 .../clang/Sema/MultiplexExternalSemaSource.h  |   4 +-
 .../include/clang/Serialization/ASTBitCodes.h |   2 +
 clang/include/clang/Serialization/ASTReader.h |   4 +-
 clang/lib/AST/DeclTemplate.cpp                |  85 ++--
 clang/lib/AST/ExternalASTSource.cpp           |  10 +-
 clang/lib/AST/ODRHash.cpp                     |  10 -
 .../lib/Sema/MultiplexExternalSemaSource.cpp  |  13 +-
 clang/lib/Serialization/ASTCommon.h           |   1 -
 clang/lib/Serialization/ASTReader.cpp         |  41 +-
 clang/lib/Serialization/ASTReaderDecl.cpp     |  80 +---
 clang/lib/Serialization/ASTReaderInternals.h  |   3 +-
 clang/lib/Serialization/ASTWriter.cpp         |  49 +--
 clang/lib/Serialization/ASTWriterDecl.cpp     |  53 +--
 clang/lib/Serialization/CMakeLists.txt        |   1 +
 .../Serialization/TemplateArgumentHasher.cpp  | 409 ++++++++++++++++++
 .../Serialization/TemplateArgumentHasher.h    |  34 ++
 clang/test/Modules/cxx-templates.cpp          |   8 +-
 .../Modules/recursive-instantiations.cppm     |  40 ++
 .../test/OpenMP/target_parallel_ast_print.cpp |   4 -
 clang/test/OpenMP/target_teams_ast_print.cpp  |   4 -
 clang/test/OpenMP/task_ast_print.cpp          |   4 -
 clang/test/OpenMP/teams_ast_print.cpp         |   4 -
 24 files changed, 616 insertions(+), 287 deletions(-)
 create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.cpp
 create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.h
 create mode 100644 clang/test/Modules/recursive-instantiations.cppm

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 7d707ed45cf5f6..336340e6dc6641 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -262,9 +262,6 @@ class TemplateArgumentList final
   TemplateArgumentList(const TemplateArgumentList &) = delete;
   TemplateArgumentList &operator=(const TemplateArgumentList &) = delete;
 
-  /// Create hash for the given arguments.
-  static unsigned ComputeODRHash(ArrayRef<TemplateArgument> Args);
-
   /// Create a new template argument list that copies the given set of
   /// template arguments.
   static TemplateArgumentList *CreateCopy(ASTContext &Context,
@@ -738,25 +735,6 @@ class RedeclarableTemplateDecl : public TemplateDecl,
   }
 
   void anchor() override;
-  struct LazySpecializationInfo {
-    GlobalDeclID DeclID = GlobalDeclID();
-    unsigned ODRHash = ~0U;
-    bool IsPartial = false;
-    LazySpecializationInfo(GlobalDeclID ID, unsigned Hash = ~0U,
-                           bool Partial = false)
-        : DeclID(ID), ODRHash(Hash), IsPartial(Partial) {}
-    LazySpecializationInfo() {}
-    bool operator<(const LazySpecializationInfo &Other) const {
-      return DeclID < Other.DeclID;
-    }
-    bool operator==(const LazySpecializationInfo &Other) const {
-      assert((DeclID != Other.DeclID || ODRHash == Other.ODRHash) &&
-             "Hashes differ!");
-      assert((DeclID != Other.DeclID || IsPartial == Other.IsPartial) &&
-             "Both must be the same kinds!");
-      return DeclID == Other.DeclID;
-    }
-  };
 
 protected:
   template <typename EntryType> struct SpecEntryTraits {
@@ -800,16 +778,20 @@ class RedeclarableTemplateDecl : public TemplateDecl,
 
   void loadLazySpecializationsImpl(bool OnlyPartial = false) const;
 
-  void loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
+  bool loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
                                    TemplateParameterList *TPL = nullptr) const;
 
-  Decl *loadLazySpecializationImpl(LazySpecializationInfo &LazySpecInfo) const;
-
   template <class EntryType, typename ...ProfileArguments>
   typename SpecEntryTraits<EntryType>::DeclType*
   findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
                          void *&InsertPos, ProfileArguments &&...ProfileArgs);
 
+  template <class EntryType, typename... ProfileArguments>
+  typename SpecEntryTraits<EntryType>::DeclType *
+  findSpecializationLocally(llvm::FoldingSetVector<EntryType> &Specs,
+                            void *&InsertPos,
+                            ProfileArguments &&...ProfileArgs);
+
   template <class Derived, class EntryType>
   void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
                              EntryType *Entry, void *InsertPos);
diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h
index 5f4f9a9a8d681e..9f968ba05b4466 100644
--- a/clang/include/clang/AST/ExternalASTSource.h
+++ b/clang/include/clang/AST/ExternalASTSource.h
@@ -155,11 +155,15 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
   /// Load all the external specializations for the Decl \param D if \param
   /// OnlyPartial is false. Otherwise, load all the external **partial**
   /// specializations for the \param D.
-  virtual void LoadExternalSpecializations(const Decl *D, bool OnlyPartial);
+  ///
+  /// Return true if any new specializations get loaded. Return false otherwise.
+  virtual bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial);
 
   /// Load all the specializations for the Decl \param D with the same template
   /// args specified by \param TemplateArgs.
-  virtual void
+  ///
+  /// Return true if any new specializations get loaded. Return false otherwise.
+  virtual bool
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs);
 
diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
index 78bbbaf2d7b5c6..0c92c52854c9e7 100644
--- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h
+++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
@@ -97,9 +97,9 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
   bool FindExternalVisibleDeclsByName(const DeclContext *DC,
                                       DeclarationName Name) override;
 
-  void LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
+  bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
 
-  void
+  bool
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs) override;
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index cb3ed6c1ecbb7c..b045c4f235cef0 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -737,6 +737,8 @@ enum ASTRecordTypes {
 
   /// Record code for updated specialization
   UPDATE_SPECIALIZATION = 73,
+  
+  CXX_ADDED_TEMPLATE_SPECIALIZATION = 74,
 };
 
 /// 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 6306d4f08e81fa..12f08386e2329d 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2098,9 +2098,9 @@ class ASTReader
                                       unsigned BlockID,
                                       uint64_t *StartOfBlockOffset = nullptr);
 
-  void LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
+  bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
 
-  void
+  bool
   LoadExternalSpecializations(const Decl *D,
                               ArrayRef<TemplateArgument> TemplateArgs) override;
 
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index a73af9c7785320..7fc0f02ce0a934 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -362,63 +362,30 @@ void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
   ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
                                               OnlyPartial);
   return;
-
-  // Grab the most recent declaration to ensure we've loaded any lazy
-  // redeclarations of this template.
-  CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
-  if (auto *Specs = CommonBasePtr->LazySpecializations) {
-    if (!OnlyPartial)
-      CommonBasePtr->LazySpecializations = nullptr;
-    unsigned N = Specs[0].DeclID.getRawValue();
-    for (unsigned I = 0; I != N; ++I) {
-      // Skip over already loaded specializations.
-      if (!Specs[I + 1].ODRHash)
-        continue;
-      if (!OnlyPartial || Specs[I + 1].IsPartial)
-        (void)loadLazySpecializationImpl(Specs[I + 1]);
-    }
-  }
-}
-
-Decl *RedeclarableTemplateDecl::loadLazySpecializationImpl(
-    LazySpecializationInfo &LazySpecInfo) const {
-  llvm_unreachable("We don't use LazySpecializationInfo any more");
-
-  GlobalDeclID ID = LazySpecInfo.DeclID;
-  assert(ID.isValid() && "Loading already loaded specialization!");
-  // Note that we loaded the specialization.
-  LazySpecInfo.DeclID = GlobalDeclID();
-  LazySpecInfo.ODRHash = LazySpecInfo.IsPartial = 0;
-  return getASTContext().getExternalSource()->GetExternalDecl(ID);
 }
 
-void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
+bool RedeclarableTemplateDecl::loadLazySpecializationsImpl(
     ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL) const {
   auto *ExternalSource = getASTContext().getExternalSource();
   if (!ExternalSource)
-    return;
+    return false;
 
-  ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), Args);
-  return;
+  // If TPL is not null, it implies that we're loading specializations for
+  // partial templates. We need to load all specializations in such cases.
+  if (TPL)
+    return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
+                                                       /*OnlyPartial=*/false);
 
-  CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
-  if (auto *Specs = CommonBasePtr->LazySpecializations) {
-    unsigned Hash = TemplateArgumentList::ComputeODRHash(Args);
-    unsigned N = Specs[0].DeclID.getRawValue();
-    for (unsigned I = 0; I != N; ++I)
-      if (Specs[I + 1].ODRHash && Specs[I + 1].ODRHash == Hash)
-        (void)loadLazySpecializationImpl(Specs[I + 1]);
-  }
+  return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
+                                                     Args);
 }
 
-template<class EntryType, typename... ProfileArguments>
+template <class EntryType, typename... ProfileArguments>
 typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
-RedeclarableTemplateDecl::findSpecializationImpl(
+RedeclarableTemplateDecl::findSpecializationLocally(
     llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos,
-    ProfileArguments&&... ProfileArgs) {
-  using SETraits = SpecEntryTraits<EntryType>;
-
-  loadLazySpecializationsImpl(std::forward<ProfileArguments>(ProfileArgs)...);
+    ProfileArguments &&...ProfileArgs) {
+  using SETraits = RedeclarableTemplateDecl::SpecEntryTraits<EntryType>;
 
   llvm::FoldingSetNodeID ID;
   EntryType::Profile(ID, std::forward<ProfileArguments>(ProfileArgs)...,
@@ -427,6 +394,24 @@ RedeclarableTemplateDecl::findSpecializationImpl(
   return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr;
 }
 
+template <class EntryType, typename... ProfileArguments>
+typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
+RedeclarableTemplateDecl::findSpecializationImpl(
+    llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos,
+    ProfileArguments &&...ProfileArgs) {
+
+  if (auto *Found = findSpecializationLocally(
+          Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...))
+    return Found;
+
+  if (!loadLazySpecializationsImpl(
+          std::forward<ProfileArguments>(ProfileArgs)...))
+    return nullptr;
+
+  return findSpecializationLocally(
+      Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...);
+}
+
 template<class Derived, class EntryType>
 void RedeclarableTemplateDecl::addSpecializationImpl(
     llvm::FoldingSetVector<EntryType> &Specializations, EntryType *Entry,
@@ -955,14 +940,6 @@ TemplateArgumentList::CreateCopy(ASTContext &Context,
   return new (Mem) TemplateArgumentList(Args);
 }
 
-unsigned TemplateArgumentList::ComputeODRHash(ArrayRef<TemplateArgument> Args) {
-  ODRHash Hasher;
-  for (TemplateArgument TA : Args)
-    Hasher.AddTemplateArgument(TA);
-
-  return Hasher.CalculateHash();
-}
-
 FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
     ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
     TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp
index 122014bfeb2321..0eeb549d8cabf3 100644
--- a/clang/lib/AST/ExternalASTSource.cpp
+++ b/clang/lib/AST/ExternalASTSource.cpp
@@ -98,10 +98,14 @@ ExternalASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC,
   return false;
 }
 
-void ExternalASTSource::LoadExternalSpecializations(const Decl *D, bool) {}
+bool ExternalASTSource::LoadExternalSpecializations(const Decl *D, bool) {
+  return false;
+}
 
-void ExternalASTSource::LoadExternalSpecializations(
-    const Decl *D, ArrayRef<TemplateArgument>) {}
+bool ExternalASTSource::LoadExternalSpecializations(
+    const Decl *D, ArrayRef<TemplateArgument>) {
+  return false;
+}
 
 void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {}
 
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index a8a3a5200d61ed..c73570e405ab68 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -819,16 +819,6 @@ void ODRHash::AddDecl(const Decl *D) {
 
   AddDeclarationName(ND->getDeclName());
 
-  const auto *Specialization =
-            dyn_cast<ClassTemplateSpecializationDecl>(D);
-  AddBoolean(Specialization);
-  if (Specialization) {
-    const TemplateArgumentList &List = Specialization->getTemplateArgs();
-    ID.AddInteger(List.size());
-    for (const TemplateArgument &TA : List.asArray())
-      AddTemplateArgument(TA);
-  }
-
   // If this was a specialization we should take into account its template
   // arguments. This helps to reduce collisions coming when visiting template
   // specialization types (eg. when processing type template arguments).
diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
index f39463712ce81f..54944267b4868a 100644
--- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp
+++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
@@ -115,16 +115,21 @@ FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) {
   return AnyDeclsFound;
 }
 
-void MultiplexExternalSemaSource::LoadExternalSpecializations(
+bool MultiplexExternalSemaSource::LoadExternalSpecializations(
     const Decl *D, bool OnlyPartial) {
+  bool Loaded = false;
   for (size_t i = 0; i < Sources.size(); ++i)
-    Sources[i]->LoadExternalSpecializations(D, OnlyPartial);
+    Loaded |= Sources[i]->LoadExternalSpecializations(D, OnlyPartial);
+  return Loaded;
 }
 
-void MultiplexExternalSemaSource::LoadExternalSpecializations(
+bool MultiplexExternalSemaSource::LoadExternalSpecializations(
     const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
+  bool AnyNewSpecsLoaded = false;
   for (size_t i = 0; i < Sources.size(); ++i)
-    Sources[i]->LoadExternalSpecializations(D, TemplateArgs);
+    AnyNewSpecsLoaded |=
+        Sources[i]->LoadExternalSpecializations(D, TemplateArgs);
+  return AnyNewSpecsLoaded;
 }
 
 void MultiplexExternalSemaSource::completeVisibleDeclsMap(const DeclContext *DC){
diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h
index 2a765eafe08951..7c9ec884ea049d 100644
--- a/clang/lib/Serialization/ASTCommon.h
+++ b/clang/lib/Serialization/ASTCommon.h
@@ -24,7 +24,6 @@ namespace serialization {
 
 enum DeclUpdateKind {
   UPD_CXX_ADDED_IMPLICIT_MEMBER,
-  UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
   UPD_CXX_ADDED_ANONYMOUS_NAMESPACE,
   UPD_CXX_ADDED_FUNCTION_DEFINITION,
   UPD_CXX_ADDED_VAR_DEFINITION,
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 75d81a25dd3ac3..f6f7969314bf54 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -12,6 +12,7 @@
 
 #include "ASTCommon.h"
 #include "ASTReaderInternals.h"
+#include "TemplateArgumentHasher.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTMutationListener.h"
@@ -3542,7 +3543,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       break;
     }
 
-    case UPDATE_SPECIALIZATION: {
+    case CXX_ADDED_TEMPLATE_SPECIALIZATION: {
       unsigned Idx = 0;
       GlobalDeclID ID = ReadDeclID(F, Record, Idx);
       auto *Data = (const unsigned char *)Blob.data();
@@ -7747,8 +7748,14 @@ void ASTReader::CompleteRedeclChain(const Decl *D) {
     }
   }
 
-  if (Template)
-    Template->loadLazySpecializationsImpl(Args);
+  if (Template) {
+    // For partitial specialization, load all the specializations for safety.
+    if (isa<ClassTemplatePartialSpecializationDecl,
+            VarTemplatePartialSpecializationDecl>(D))
+      Template->loadLazySpecializationsImpl();
+    else
+      Template->loadLazySpecializationsImpl(Args);
+  }
 }
 
 CXXCtorInitializer **
@@ -8129,12 +8136,12 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) {
   return ReadStmtFromStream(*Loc.F);
 }
 
-void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
+bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
   assert(D);
 
   auto It = SpecializationsLookups.find(D);
   if (It == SpecializationsLookups.end())
-    return;
+    return false;
 
   // Get Decl may violate the iterator from SpecializationsLookups so we store
   // the DeclIDs in ahead.
@@ -8146,29 +8153,43 @@ void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
   if (!OnlyPartial)
     SpecializationsLookups.erase(It);
 
+  bool NewSpecsFound = false;
   Deserializing LookupResults(this);
   for (auto &Info : Infos)
-    if (!OnlyPartial || Info.IsPartial)
+    if (!OnlyPartial || Info.IsPartial) {
+      if (GetExistingDecl(Info.ID))
+        continue;
+      NewSpecsFound = true;
       GetDecl(Info.ID);
+    }
+
+  return NewSpecsFound;
 }
 
-void ASTReader::LoadExternalSpecializations(
+bool ASTReader::LoadExternalSpecializations(
     const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
   assert(D);
 
   auto It = SpecializationsLookups.find(D);
   if (It == SpecializationsLookups.end())
-    return;
+    return false;
 
   Deserializing LookupResults(this);
-  auto HashValue = TemplateArgumentList::ComputeODRHash(TemplateArgs);
+  auto HashValue = StableHashForTemplateArguments(TemplateArgs);
 
   // Get Decl may violate the iterator from SpecializationsLookups
   llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos =
       It->second.Table.find(HashValue);
 
-  for (auto &Info : Infos)
+  bool NewSpecsFound = false;
+  for (auto &Info : Infos) {
+    if (GetExistingDecl(Info.ID))
+      continue;
+    NewSpecsFound = true;
     GetDecl(Info.ID);
+  }
+
+  return NewSpecsFound;
 }
 
 void ASTReader::FindExternalLexicalDecls(
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 87c491a977ffeb..9ac712d8625db8 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -187,21 +187,6 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
 
   std::string readString() { return Record.readString(); }
 
-  using LazySpecializationInfo =
-      RedeclarableTemplateDecl::LazySpecializationInfo;
-
-  LazySpecializationInfo ReadLazySpecializationInfo() {
-    GlobalDeclID ID = readDeclID();
-    unsigned Hash = Record.readInt();
-    bool IsPartial = Record.readInt();
-    return LazySpecializationInfo(ID, Hash, IsPartial);
-  }
-
-  void readDeclIDList(SmallVectorImpl<LazySpecializationInfo> &IDs) {
-    for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I)
-      IDs.push_back(ReadLazySpecializationInfo());
-  }
-
   Decl *readDecl() { return Record.readDecl(); }
 
   template <typename T> T *readDeclAs() { return Record.readDeclAs<T>(); }
@@ -294,30 +279,6 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
       : Reader(Reader), MergeImpl(Reader), Record(Record), Loc(Loc),
         ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {}
 
-  template <typename T>
-  static void
-  AddLazySpecializations(T *D, SmallVectorImpl<LazySpecializationInfo> &IDs) {
-    if (IDs.empty())
-      return;
-
-    // FIXME: We should avoid this pattern of getting the ASTContext.
-    ASTContext &C = D->getASTContext();
-
-    auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations;
-
-    if (auto &Old = LazySpecializations) {
-      IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].DeclID.getRawValue());
-      llvm::sort(IDs);
-      IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end());
-    }
-    auto *Result = new (C)
-        RedeclarableTemplateDecl::LazySpecializationInfo[1 + IDs.size()];
-    Result->DeclID = GlobalDeclID(IDs.size());
-    std::copy(IDs.begin(), IDs.end(), Result + 1);
-
-    LazySpecializations = Result;
-  }
-
   template <typename DeclT>
   static Decl *getMostRecentDeclImpl(Redeclarable<DeclT> *D);
   static Decl *getMostRecentDeclImpl(...);
@@ -348,9 +309,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
   void ReadFunctionDefinition(FunctionDecl *FD);
   void Visit(Decl *D);
 
-  void UpdateDecl(
-      Decl *D,
-      SmallVectorImpl<RedeclarableTemplateDecl::LazySpecializationInfo> &);
+  void UpdateDecl(Decl *D);
 
   static void setNextObjCCategory(ObjCCategoryDecl *Cat,
                                   ObjCCategoryDecl *Next) {
@@ -2479,9 +2438,6 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    SmallVector<LazySpecializationInfo, 32> SpecIDs;
-    readDeclIDList(SpecIDs);
-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
     ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
   }
 
@@ -2508,9 +2464,6 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    SmallVector<LazySpecializationInfo, 32> SpecIDs;
-    readDeclIDList(SpecIDs);
-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
     ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
   }
 }
@@ -2610,9 +2563,6 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
 
   if (ThisDeclID == Redecl.getFirstID()) {
     // This FunctionTemplateDecl owns a CommonPtr; read it.
-    SmallVector<LazySpecializationInfo, 32> SpecIDs;
-    readDeclIDList(SpecIDs);
-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
     ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
   }
 }
@@ -4313,10 +4263,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
   ProcessingUpdatesRAIIObj ProcessingUpdates(*this);
   DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID);
 
-  using LazySpecializationInfo =
-      RedeclarableTemplateDecl::LazySpecializationInfo;
-  llvm::SmallVector<LazySpecializationInfo, 8> PendingLazySpecializationIDs;
-
   if (UpdI != DeclUpdateOffsets.end()) {
     auto UpdateOffsets = std::move(UpdI->second);
     DeclUpdateOffsets.erase(UpdI);
@@ -4353,7 +4299,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
 
       ASTDeclReader Reader(*this, Record, RecordLocation(F, Offset), ID,
                            SourceLocation());
-      Reader.UpdateDecl(D, PendingLazySpecializationIDs);
+      Reader.UpdateDecl(D);
 
       // We might have made this declaration interesting. If so, remember that
       // we need to hand it off to the consumer.
@@ -4363,19 +4309,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
       }
     }
   }
-  // Add the lazy specializations to the template.
-  assert((PendingLazySpecializationIDs.empty() || isa<ClassTemplateDecl>(D) ||
-          isa<FunctionTemplateDecl, VarTemplateDecl>(D)) &&
-         "Must not have pending specializations");
-  /*
-  if (auto *CTD = dyn_cast<ClassTemplateDecl>(D))
-    ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs);
-  else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
-    ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs);
-  else if (auto *VTD = dyn_cast<VarTemplateDecl>(D))
-    ASTDeclReader::AddLazySpecializations(VTD, PendingLazySpecializationIDs);
-  */
-  PendingLazySpecializationIDs.clear();
 
   // Load the pending visible updates for this decl context, if it has any.
   auto I = PendingVisibleUpdates.find(ID);
@@ -4602,9 +4535,7 @@ static void forAllLaterRedecls(DeclT *D, Fn F) {
   }
 }
 
-void ASTDeclReader::UpdateDecl(
-    Decl *D,
-    SmallVectorImpl<LazySpecializationInfo> &PendingLazySpecializationIDs) {
+void ASTDeclReader::UpdateDecl(Decl *D) {
   while (Record.getIdx() < Record.size()) {
     switch ((DeclUpdateKind)Record.readInt()) {
     case UPD_CXX_ADDED_IMPLICIT_MEMBER: {
@@ -4615,11 +4546,6 @@ void ASTDeclReader::UpdateDecl(
       break;
     }
 
-    case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
-      // It will be added to the template's lazy specialization set.
-      PendingLazySpecializationIDs.push_back(ReadLazySpecializationInfo());
-      break;
-
     case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: {
       auto *Anon = readDeclAs<NamespaceDecl>();
 
diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h
index b921d8d174c3a2..840ddf81e0201a 100644
--- a/clang/lib/Serialization/ASTReaderInternals.h
+++ b/clang/lib/Serialization/ASTReaderInternals.h
@@ -125,11 +125,10 @@ struct LazySpecializationInfo {
   // Whether or not this specialization is partial.
   bool IsPartial;
 
-  bool operator==(const LazySpecializationInfo &Other) {
+  bool operator==(const LazySpecializationInfo &Other) const {
     assert(ID != Other.ID || IsPartial == Other.IsPartial);
     return ID == Other.ID;
   }
-
   // Records the size record in OnDiskHashTable.
   // sizeof() may return 8 due to align requirements.
   static constexpr unsigned Length = sizeof(DeclID) + sizeof(IsPartial);
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index adfe9fc8b369d6..d4898b3dac3f52 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -13,6 +13,7 @@
 #include "ASTCommon.h"
 #include "ASTReaderInternals.h"
 #include "MultiOnDiskHashTable.h"
+#include "TemplateArgumentHasher.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTUnresolvedSet.h"
 #include "clang/AST/AbstractTypeWriter.h"
@@ -4125,7 +4126,7 @@ class LazySpecializationInfoLookupTrait {
   explicit LazySpecializationInfoLookupTrait(ASTWriter &Writer)
       : Writer(Writer) {}
 
-  template <typename Col> data_type getData(Col &&C) {
+  template <typename Col, typename Col2> data_type getData(Col &&C, Col2 &ExistingInfo) {
     unsigned Start = Specs.size();
     for (auto *D : C) {
       bool IsPartial = isa<ClassTemplatePartialSpecializationDecl,
@@ -4135,6 +4136,8 @@ class LazySpecializationInfoLookupTrait {
       Specs.push_back({GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()),
                        IsPartial});
     }
+    for (const serialization::reader::LazySpecializationInfo &Info : ExistingInfo)
+      Specs.push_back(Info);
     return std::make_pair(Start, Specs.size());
   }
 
@@ -4203,7 +4206,7 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) {
   else
     llvm_unreachable("New Specialization Kind?");
 
-  return TemplateArgumentList::ComputeODRHash(Args);
+  return StableHashForTemplateArguments(Args);
 }
 } // namespace
 
@@ -4234,11 +4237,23 @@ void ASTWriter::GenerateSpecializationInfoLookupTable(
     Iter->second.push_back(cast<NamedDecl>(Specialization));
   }
 
-  for (auto Iter : SpecializationMaps)
-    Generator.insert(Iter.first, Trait.getData(Iter.second), Trait);
-
   auto *Lookups =
       Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr;
+
+  for (auto &[HashValue, Specs] : SpecializationMaps) {
+    SmallVector<serialization::reader::LazySpecializationInfo, 16> ExisitingSpecs;
+    // We have to merge the lookup table manually here. We can't depend on the merge mechanism
+    // offered by clang::serialization::MultiOnDiskHashTableGenerator since that generator
+    // assumes the we'll get the same value with the same key.
+    // And also underlying llvm::OnDiskChainedHashTableGenerator assumes that we won't insert
+    // the values with the same key twice.
+    // So we have to merge the lookup table here manually.
+    if (Lookups)
+      ExisitingSpecs = Lookups->Table.find(HashValue);
+
+    Generator.insert(HashValue, Trait.getData(Specs, ExisitingSpecs), Trait);
+  }
+
   Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr);
 }
 
@@ -5921,7 +5936,7 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
 
 void ASTWriter::WriteSpecializationsUpdates() {
   auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
-  Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_SPECIALIZATION));
+  Abv->Add(llvm::BitCodeAbbrevOp(CXX_ADDED_TEMPLATE_SPECIALIZATION));
   Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
   Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
   auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv));
@@ -5934,7 +5949,8 @@ void ASTWriter::WriteSpecializationsUpdates() {
                                           LookupTable);
 
     // Write the lookup table
-    RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D).getRawValue()};
+    RecordData::value_type Record[] = {CXX_ADDED_TEMPLATE_SPECIALIZATION,
+                                       getDeclID(D).getRawValue()};
     Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable);
   }
 }
@@ -5972,25 +5988,6 @@ void ASTWriter::WriteDeclUpdatesBlocks(ASTContext &Context,
         assert(Update.getDecl() && "no decl to add?");
         Record.AddDeclRef(Update.getDecl());
         break;
-      case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: {
-        const Decl *Spec = Update.getDecl();
-        assert(Spec && "no decl to add?");
-        Record.AddDeclRef(Spec);
-        ArrayRef<TemplateArgument> Args;
-        if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Spec))
-          Args = CTSD->getTemplateArgs().asArray();
-        else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Spec))
-          Args = VTSD->getTemplateArgs().asArray();
-        else if (auto *FD = dyn_cast<FunctionDecl>(Spec))
-          Args = FD->getTemplateSpecializationArgs()->asArray();
-        assert(Args.size());
-        Record.push_back(TemplateArgumentList::ComputeODRHash(Args));
-        bool IsPartialSpecialization =
-            isa<ClassTemplatePartialSpecializationDecl>(Spec) ||
-            isa<VarTemplatePartialSpecializationDecl>(Spec);
-        Record.push_back(IsPartialSpecialization);
-        break;
-      }
       case UPD_CXX_ADDED_FUNCTION_DEFINITION:
       case UPD_CXX_ADDED_VAR_DEFINITION:
         break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 14c2f23700e8c3..78c4f3bd62c33f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -215,24 +215,8 @@ namespace clang {
       llvm::MapVector<ModuleFile *, const Decl *> Firsts;
       CollectFirstDeclFromEachModule(D, /*IncludeLocal*/ true, Firsts);
 
-      for (const auto &F : Firsts) {
+      for (const auto &F : Firsts)
         SpecsInMap.push_back(F.second);
-
-        Record.AddDeclRef(F.second);
-        ArrayRef<TemplateArgument> Args;
-        if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
-          Args = CTSD->getTemplateArgs().asArray();
-        else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
-          Args = VTSD->getTemplateArgs().asArray();
-        else if (auto *FD = dyn_cast<FunctionDecl>(D))
-          Args = FD->getTemplateSpecializationArgs()->asArray();
-        assert(Args.size());
-        Record.push_back(TemplateArgumentList::ComputeODRHash(Args));
-        bool IsPartialSpecialization =
-            isa<ClassTemplatePartialSpecializationDecl>(D) ||
-            isa<VarTemplatePartialSpecializationDecl>(D);
-        Record.push_back(IsPartialSpecialization);
-      }
     }
 
     /// Get the specialization decl from an entry in the specialization list.
@@ -259,23 +243,12 @@ namespace clang {
       // If we have any lazy specializations, and the external AST source is
       // our chained AST reader, we can just write out the DeclIDs. Otherwise,
       // we need to resolve them to actual declarations.
-      if (Writer.Chain != Record.getASTContext().getExternalSource() &&
-          Common->LazySpecializations) {
+      if (Writer.Chain != Writer.Context->getExternalSource() && Writer.Chain &&
+          Writer.Chain->getLoadedSpecializationsLookupTables(D)) {
         D->LoadLazySpecializations();
-        assert(!Common->LazySpecializations);
+        assert(!Writer.Chain->getLoadedSpecializationsLookupTables(D));
       }
 
-      using LazySpecializationInfo =
-          RedeclarableTemplateDecl::LazySpecializationInfo;
-      ArrayRef<LazySpecializationInfo> LazySpecializations;
-      if (auto *LS = Common->LazySpecializations)
-        LazySpecializations =
-            llvm::ArrayRef(LS + 1, LS[0].DeclID.getRawValue());
-
-      // Add a slot to the record for the number of specializations.
-      unsigned I = Record.size();
-      Record.push_back(0);
-
       // AddFirstDeclFromEachModule might trigger deserialization, invalidating
       // *Specializations iterators.
       llvm::SmallVector<const Decl*, 16> Specs;
@@ -291,21 +264,6 @@ namespace clang {
         AddFirstSpecializationDeclFromEachModule(D, SpecsInOnDiskMap);
       }
 
-      // We don't need to insert LazySpecializations to SpecsInOnDiskMap,
-      // since we'll handle that in GenerateSpecializationInfoLookupTable.
-      for (auto &SpecInfo : LazySpecializations) {
-        Record.push_back(SpecInfo.DeclID.getRawValue());
-        Record.push_back(SpecInfo.ODRHash);
-        Record.push_back(SpecInfo.IsPartial);
-      }
-
-      // Update the size entry we added earlier. We linerized the
-      // LazySpecializationInfo members and we need to adjust the size as we
-      // will read them always together.
-      assert((Record.size() - I - 1) % 3 == 0 &&
-             "Must be divisible by LazySpecializationInfo count!");
-      Record[I] = (Record.size() - I - 1) / 3;
-
       Record.AddOffset(
           Writer.WriteSpecializationInfoLookupTable(D, SpecsInOnDiskMap));
     }
@@ -328,9 +286,6 @@ namespace clang {
       if (Writer.getFirstLocalDecl(Specialization) != Specialization)
         return;
 
-      Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate(
-          UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization));
-
       Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back(
           cast<NamedDecl>(Specialization));
     }
diff --git a/clang/lib/Serialization/CMakeLists.txt b/clang/lib/Serialization/CMakeLists.txt
index 99c47c15a2f479..b1fc0345047f24 100644
--- a/clang/lib/Serialization/CMakeLists.txt
+++ b/clang/lib/Serialization/CMakeLists.txt
@@ -23,6 +23,7 @@ add_clang_library(clangSerialization
   ModuleManager.cpp
   PCHContainerOperations.cpp
   ObjectFilePCHContainerReader.cpp
+  TemplateArgumentHasher.cpp
 
   ADDITIONAL_HEADERS
   ASTCommon.h
diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp b/clang/lib/Serialization/TemplateArgumentHasher.cpp
new file mode 100644
index 00000000000000..598f098f526d0f
--- /dev/null
+++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp
@@ -0,0 +1,409 @@
+//===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TemplateArgumentHasher.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/TypeVisitor.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/FoldingSet.h"
+
+using namespace clang;
+
+namespace {
+
+class TemplateArgumentHasher {
+  // If we bail out during the process of calculating hash values for
+  // template arguments for any reason. We're allowed to do it since
+  // TemplateArgumentHasher are only required to give the same hash value
+  // for the same template arguments, but not required to give different
+  // hash value for different template arguments.
+  //
+  // So in the worst case, it is still a valid implementation to give all
+  // inputs the same BailedOutValue as output.
+  bool BailedOut = false;
+  static constexpr unsigned BailedOutValue = 0x12345678;
+
+  llvm::FoldingSetNodeID ID;
+
+public:
+  TemplateArgumentHasher() = default;
+
+  void AddTemplateArgument(TemplateArgument TA);
+
+  void AddInteger(unsigned V) { ID.AddInteger(V); }
+
+  unsigned getValue() {
+    if (BailedOut)
+      return BailedOutValue;
+
+    return ID.computeStableHash();
+  }
+
+  void setBailedOut() { BailedOut = true; }
+
+  void AddType(const Type *T);
+  void AddQualType(QualType T);
+  void AddDecl(const Decl *D);
+  void AddStructuralValue(const APValue &);
+  void AddTemplateName(TemplateName Name);
+  void AddDeclarationName(DeclarationName Name);
+  void AddIdentifierInfo(const IdentifierInfo *II);
+};
+
+void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) {
+  const auto Kind = TA.getKind();
+  AddInteger(Kind);
+
+  switch (Kind) {
+  case TemplateArgument::Null:
+    llvm_unreachable("Expected valid TemplateArgument");
+  case TemplateArgument::Type:
+    AddQualType(TA.getAsType());
+    break;
+  case TemplateArgument::Declaration:
+    AddDecl(TA.getAsDecl());
+    break;
+  case TemplateArgument::NullPtr:
+    ID.AddPointer(nullptr);
+    break;
+  case TemplateArgument::Integral: {
+    // There are integrals (e.g.: _BitInt(128)) that cannot be represented as
+    // any builtin integral type, so we use the hash of APSInt instead.
+    TA.getAsIntegral().Profile(ID);
+    break;
+  }
+  case TemplateArgument::StructuralValue:
+    AddQualType(TA.getStructuralValueType());
+    AddStructuralValue(TA.getAsStructuralValue());
+    break;
+  case TemplateArgument::Template:
+  case TemplateArgument::TemplateExpansion:
+    AddTemplateName(TA.getAsTemplateOrTemplatePattern());
+    break;
+  case TemplateArgument::Expression:
+    // If we meet expression in template argument, it implies
+    // that the template is still dependent. It is meaningless
+    // to get a stable hash for the template. Bail out simply.
+    BailedOut = true;
+    break;
+  case TemplateArgument::Pack:
+    AddInteger(TA.pack_size());
+    for (auto SubTA : TA.pack_elements()) {
+      AddTemplateArgument(SubTA);
+    }
+    break;
+  }
+}
+
+void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) {
+  auto Kind = Value.getKind();
+  AddInteger(Kind);
+
+  // 'APValue::Profile' uses pointer values to make hash for LValue and
+  // MemberPointer, but they differ from one compiler invocation to another.
+  // It may be difficult to handle such cases. Bail out simply.
+
+  if (Kind == APValue::LValue || Kind == APValue::MemberPointer) {
+    BailedOut = true;
+    return;
+  }
+
+  Value.Profile(ID);
+}
+
+void TemplateArgumentHasher::AddTemplateName(TemplateName Name) {
+  switch (Name.getKind()) {
+  case TemplateName::Template:
+    AddDecl(Name.getAsTemplateDecl());
+    break;
+  case TemplateName::QualifiedTemplate: {
+    QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();
+    AddTemplateName(QTN->getUnderlyingTemplate());
+    break;
+  }
+  case TemplateName::OverloadedTemplate:
+  case TemplateName::AssumedTemplate:
+  case TemplateName::DependentTemplate:
+  case TemplateName::SubstTemplateTemplateParm:
+  case TemplateName::SubstTemplateTemplateParmPack:
+    BailedOut = true;
+    break;
+  case TemplateName::UsingTemplate: {
+    UsingShadowDecl *USD = Name.getAsUsingShadowDecl();
+    if (USD)
+      AddDecl(USD->getTargetDecl());
+    else
+      BailedOut = true;
+    break;
+  }
+  case TemplateName::DeducedTemplate:
+    AddTemplateName(Name.getAsDeducedTemplateName()->getUnderlying());
+    break;
+  }
+}
+
+void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) {
+  assert(II && "Expecting non-null pointer.");
+  ID.AddString(II->getName());
+}
+
+void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {
+  if (Name.isEmpty())
+    return;
+
+  switch (Name.getNameKind()) {
+  case DeclarationName::Identifier:
+    AddIdentifierInfo(Name.getAsIdentifierInfo());
+    break;
+  case DeclarationName::ObjCZeroArgSelector:
+  case DeclarationName::ObjCOneArgSelector:
+  case DeclarationName::ObjCMultiArgSelector:
+    BailedOut = true;
+    break;
+  case DeclarationName::CXXConstructorName:
+  case DeclarationName::CXXDestructorName:
+    AddQualType(Name.getCXXNameType());
+    break;
+  case DeclarationName::CXXOperatorName:
+    AddInteger(Name.getCXXOverloadedOperator());
+    break;
+  case DeclarationName::CXXLiteralOperatorName:
+    AddIdentifierInfo(Name.getCXXLiteralIdentifier());
+    break;
+  case DeclarationName::CXXConversionFunctionName:
+    AddQualType(Name.getCXXNameType());
+    break;
+  case DeclarationName::CXXUsingDirective:
+    break;
+  case DeclarationName::CXXDeductionGuideName: {
+    if (auto *Template = Name.getCXXDeductionGuideTemplate())
+      AddDecl(Template);
+  }
+  }
+}
+
+void TemplateArgumentHasher::AddDecl(const Decl *D) {
+  const NamedDecl *ND = dyn_cast<NamedDecl>(D);
+  if (!ND) {
+    BailedOut = true;
+    return;
+  }
+
+  AddDeclarationName(ND->getDeclName());
+}
+
+void TemplateArgumentHasher::AddQualType(QualType T) {
+  if (T.isNull()) {
+    BailedOut = true;
+    return;
+  }
+  SplitQualType split = T.split();
+  AddInteger(split.Quals.getAsOpaqueValue());
+  AddType(split.Ty);
+}
+
+// Process a Type pointer.  Add* methods call back into TemplateArgumentHasher
+// while Visit* methods process the relevant parts of the Type.
+// Any unhandled type will make the hash computation bail out.
+class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> {
+  typedef TypeVisitor<TypeVisitorHelper> Inherited;
+  llvm::FoldingSetNodeID &ID;
+  TemplateArgumentHasher &Hash;
+
+public:
+  TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash)
+      : ID(ID), Hash(Hash) {}
+
+  void AddDecl(const Decl *D) {
+    if (D)
+      Hash.AddDecl(D);
+    else
+      Hash.AddInteger(0);
+  }
+
+  void AddQualType(QualType T) { Hash.AddQualType(T); }
+
+  void AddType(const Type *T) {
+    if (T)
+      Hash.AddType(T);
+    else
+      Hash.AddInteger(0);
+  }
+
+  void VisitQualifiers(Qualifiers Quals) {
+    Hash.AddInteger(Quals.getAsOpaqueValue());
+  }
+
+  void Visit(const Type *T) { Inherited::Visit(T); }
+
+  // Unhandled types. Bail out simply.
+  void VisitType(const Type *T) { Hash.setBailedOut(); }
+
+  void VisitAdjustedType(const AdjustedType *T) {
+    AddQualType(T->getOriginalType());
+  }
+
+  void VisitDecayedType(const DecayedType *T) {
+    // getDecayedType and getPointeeType are derived from getAdjustedType
+    // and don't need to be separately processed.
+    VisitAdjustedType(T);
+  }
+
+  void VisitArrayType(const ArrayType *T) {
+    AddQualType(T->getElementType());
+    Hash.AddInteger(llvm::to_underlying(T->getSizeModifier()));
+    VisitQualifiers(T->getIndexTypeQualifiers());
+  }
+  void VisitConstantArrayType(const ConstantArrayType *T) {
+    T->getSize().Profile(ID);
+    VisitArrayType(T);
+  }
+
+  void VisitAttributedType(const AttributedType *T) {
+    Hash.AddInteger(T->getAttrKind());
+    AddQualType(T->getModifiedType());
+  }
+
+  void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); }
+
+  void VisitComplexType(const ComplexType *T) {
+    AddQualType(T->getElementType());
+  }
+
+  void VisitDecltypeType(const DecltypeType *T) {
+    AddQualType(T->getUnderlyingType());
+  }
+
+  void VisitDeducedType(const DeducedType *T) {
+    AddQualType(T->getDeducedType());
+  }
+
+  void VisitAutoType(const AutoType *T) { VisitDeducedType(T); }
+
+  void VisitDeducedTemplateSpecializationType(
+      const DeducedTemplateSpecializationType *T) {
+    Hash.AddTemplateName(T->getTemplateName());
+    VisitDeducedType(T);
+  }
+
+  void VisitFunctionType(const FunctionType *T) {
+    AddQualType(T->getReturnType());
+    T->getExtInfo().Profile(ID);
+    Hash.AddInteger(T->isConst());
+    Hash.AddInteger(T->isVolatile());
+    Hash.AddInteger(T->isRestrict());
+  }
+
+  void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
+    VisitFunctionType(T);
+  }
+
+  void VisitFunctionProtoType(const FunctionProtoType *T) {
+    Hash.AddInteger(T->getNumParams());
+    for (auto ParamType : T->getParamTypes())
+      AddQualType(ParamType);
+
+    VisitFunctionType(T);
+  }
+
+  void VisitMemberPointerType(const MemberPointerType *T) {
+    AddQualType(T->getPointeeType());
+    AddType(T->getClass());
+  }
+
+  void VisitPackExpansionType(const PackExpansionType *T) {
+    AddQualType(T->getPattern());
+  }
+
+  void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); }
+
+  void VisitPointerType(const PointerType *T) {
+    AddQualType(T->getPointeeType());
+  }
+
+  void VisitReferenceType(const ReferenceType *T) {
+    AddQualType(T->getPointeeTypeAsWritten());
+  }
+
+  void VisitLValueReferenceType(const LValueReferenceType *T) {
+    VisitReferenceType(T);
+  }
+
+  void VisitRValueReferenceType(const RValueReferenceType *T) {
+    VisitReferenceType(T);
+  }
+
+  void
+  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
+    AddDecl(T->getAssociatedDecl());
+    Hash.AddTemplateArgument(T->getArgumentPack());
+  }
+
+  void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {
+    AddDecl(T->getAssociatedDecl());
+    AddQualType(T->getReplacementType());
+  }
+
+  void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); }
+
+  void VisitRecordType(const RecordType *T) { VisitTagType(T); }
+  void VisitEnumType(const EnumType *T) { VisitTagType(T); }
+
+  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
+    Hash.AddInteger(T->template_arguments().size());
+    for (const auto &TA : T->template_arguments()) {
+      Hash.AddTemplateArgument(TA);
+    }
+    Hash.AddTemplateName(T->getTemplateName());
+  }
+
+  void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
+    Hash.AddInteger(T->getDepth());
+    Hash.AddInteger(T->getIndex());
+    Hash.AddInteger(T->isParameterPack());
+  }
+
+  void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); }
+
+  void VisitElaboratedType(const ElaboratedType *T) {
+    AddQualType(T->getNamedType());
+  }
+
+  void VisitUnaryTransformType(const UnaryTransformType *T) {
+    AddQualType(T->getUnderlyingType());
+    AddQualType(T->getBaseType());
+  }
+
+  void VisitVectorType(const VectorType *T) {
+    AddQualType(T->getElementType());
+    Hash.AddInteger(T->getNumElements());
+    Hash.AddInteger(llvm::to_underlying(T->getVectorKind()));
+  }
+
+  void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }
+};
+
+void TemplateArgumentHasher::AddType(const Type *T) {
+  assert(T && "Expecting non-null pointer.");
+  TypeVisitorHelper(ID, *this).Visit(T);
+}
+
+} // namespace
+
+unsigned clang::serialization::StableHashForTemplateArguments(
+    llvm::ArrayRef<TemplateArgument> Args) {
+  TemplateArgumentHasher Hasher;
+  Hasher.AddInteger(Args.size());
+  for (TemplateArgument Arg : Args)
+    Hasher.AddTemplateArgument(Arg);
+  return Hasher.getValue();
+}
diff --git a/clang/lib/Serialization/TemplateArgumentHasher.h b/clang/lib/Serialization/TemplateArgumentHasher.h
new file mode 100644
index 00000000000000..f23f1318afbbf4
--- /dev/null
+++ b/clang/lib/Serialization/TemplateArgumentHasher.h
@@ -0,0 +1,34 @@
+//===- TemplateArgumentHasher.h - Hash Template Arguments -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/TemplateBase.h"
+
+namespace clang {
+namespace serialization {
+
+/// Calculate a stable hash value for template arguments. We guarantee that
+/// the same template arguments must have the same hashed values. But we don't
+/// guarantee that the template arguments with the same hashed value are the
+/// same template arguments.
+///
+/// ODR hashing may not be the best mechanism to hash the template
+/// arguments. ODR hashing is (or perhaps, should be) about determining whether
+/// two things are spelled the same way and have the same meaning (as required
+/// by the C++ ODR), whereas what we want here is whether they have the same
+/// meaning regardless of spelling. Maybe we can get away with reusing ODR
+/// hashing anyway, on the basis that any canonical, non-dependent template
+/// argument should have the same (invented) spelling in every translation
+/// unit, but it is not sure that's true in all cases. There may still be cases
+/// where the canonical type includes some aspect of "whatever we saw first",
+/// in which case the ODR hash can differ across translation units for
+/// non-dependent, canonical template arguments that are spelled differently
+/// but have the same meaning. But it is not easy to raise examples.
+unsigned StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args);
+
+} // namespace serialization
+} // namespace clang
diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp
index e10ba7c2ac3ef2..b7d5741e69af61 100644
--- a/clang/test/Modules/cxx-templates.cpp
+++ b/clang/test/Modules/cxx-templates.cpp
@@ -251,7 +251,7 @@ namespace Std {
 
 // CHECK-DUMP:      ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}>  col:{{.*}} in cxx_templates_common SomeTemplate
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
@@ -260,9 +260,9 @@ namespace Std {
 // CHECK-DUMP-NEXT:       CopyAssignment
 // CHECK-DUMP-NEXT:       MoveAssignment
 // CHECK-DUMP-NEXT:       Destructor
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
@@ -271,4 +271,4 @@ namespace Std {
 // CHECK-DUMP-NEXT:       CopyAssignment
 // CHECK-DUMP-NEXT:       MoveAssignment
 // CHECK-DUMP-NEXT:       Destructor
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
diff --git a/clang/test/Modules/recursive-instantiations.cppm b/clang/test/Modules/recursive-instantiations.cppm
new file mode 100644
index 00000000000000..d5854b0e647e37
--- /dev/null
+++ b/clang/test/Modules/recursive-instantiations.cppm
@@ -0,0 +1,40 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/type_traits.cppm -emit-module-interface -o %t/type_traits.pcm
+// RUN: %clang_cc1 -std=c++20 %t/test.cpp -fprebuilt-module-path=%t -verify
+
+//--- type_traits.cppm
+export module type_traits;
+
+export template <typename T>
+constexpr bool is_pod_v = __is_pod(T);
+
+//--- test.cpp
+// expected-no-diagnostics
+import type_traits;
+// Base is either void or wrapper<T>.
+template <class Base> struct wrapper : Base {};
+template <> struct wrapper<void> {};
+
+// wrap<0>::type<T> is wrapper<T>, wrap<1>::type<T> is wrapper<wrapper<T>>,
+// and so on.
+template <int N>
+struct wrap {
+  template <class Base>
+  using type = wrapper<typename wrap<N-1>::template type<Base>>;
+};
+
+template <>
+struct wrap<0> {
+  template <class Base>
+  using type = wrapper<Base>;
+};
+
+inline constexpr int kMaxRank = 40;
+template <int N, class Base = void>
+using rank = typename wrap<N>::template type<Base>;
+using rank_selector_t = rank<kMaxRank>;
+
+static_assert(is_pod_v<rank_selector_t>, "Must be POD");
diff --git a/clang/test/OpenMP/target_parallel_ast_print.cpp b/clang/test/OpenMP/target_parallel_ast_print.cpp
index 7e27ac7b92ca4a..3ee98bc525c1bd 100644
--- a/clang/test/OpenMP/target_parallel_ast_print.cpp
+++ b/clang/test/OpenMP/target_parallel_ast_print.cpp
@@ -38,10 +38,6 @@ struct S {
 // CHECK:        static int TS;
 // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
 // CHECK-NEXT: }
-// CHECK:      template<> struct S<char> {
-// CHECK:        static char TS;
-// CHECK-NEXT:   #pragma omp threadprivate(S<char>::TS)
-// CHECK-NEXT: }
 
 template <typename T, int C>
 T tmain(T argc, T *argv) {
diff --git a/clang/test/OpenMP/target_teams_ast_print.cpp b/clang/test/OpenMP/target_teams_ast_print.cpp
index 8338f2a68f9228..cc47ae92efac0f 100644
--- a/clang/test/OpenMP/target_teams_ast_print.cpp
+++ b/clang/test/OpenMP/target_teams_ast_print.cpp
@@ -40,10 +40,6 @@ struct S {
 // CHECK:        static int TS;
 // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
 // CHECK-NEXT: }
-// CHECK:      template<> struct S<long> {
-// CHECK:        static long TS;
-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
-// CHECK-NEXT: }
 
 template <typename T, int C>
 T tmain(T argc, T *argv) {
diff --git a/clang/test/OpenMP/task_ast_print.cpp b/clang/test/OpenMP/task_ast_print.cpp
index 2a6b8908a1e2dc..30fb7ab75cc87a 100644
--- a/clang/test/OpenMP/task_ast_print.cpp
+++ b/clang/test/OpenMP/task_ast_print.cpp
@@ -87,10 +87,6 @@ struct S {
 // CHECK:        static int TS;
 // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
 // CHECK-NEXT: }
-// CHECK:      template<> struct S<long> {
-// CHECK:        static long TS;
-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
-// CHECK-NEXT: }
 
 template <typename T, int C>
 T tmain(T argc, T *argv) {
diff --git a/clang/test/OpenMP/teams_ast_print.cpp b/clang/test/OpenMP/teams_ast_print.cpp
index 0087f71ac9f742..597a9b2bdbdc5d 100644
--- a/clang/test/OpenMP/teams_ast_print.cpp
+++ b/clang/test/OpenMP/teams_ast_print.cpp
@@ -27,10 +27,6 @@ struct S {
 // CHECK:        static int TS;
 // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
 // CHECK-NEXT: }
-// CHECK:      template<> struct S<long> {
-// CHECK:        static long TS;
-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
-// CHECK-NEXT: }
 
 template <typename T, int C>
 T tmain(T argc, T *argv) {

>From 4d5b9585684f008c25dc88b2b3fa55509b9627e4 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Fri, 8 Nov 2024 16:49:05 +0800
Subject: [PATCH 2/2] Split partial specializations from the lookup table

---
 clang/include/clang/AST/DeclTemplate.h        |   7 --
 .../include/clang/Serialization/ASTBitCodes.h |  11 +-
 clang/include/clang/Serialization/ASTReader.h |  30 +++--
 clang/include/clang/Serialization/ASTWriter.h |   9 +-
 clang/lib/Serialization/ASTReader.cpp         | 111 +++++++++++++-----
 clang/lib/Serialization/ASTReaderDecl.cpp     |  29 +++--
 clang/lib/Serialization/ASTReaderInternals.h  |  43 +------
 clang/lib/Serialization/ASTWriter.cpp         |  55 +++++----
 clang/lib/Serialization/ASTWriterDecl.cpp     |  63 +++++++---
 9 files changed, 216 insertions(+), 142 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 336340e6dc6641..dd92d40b804232 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -806,13 +806,6 @@ class RedeclarableTemplateDecl : public TemplateDecl,
     /// was explicitly specialized.
     llvm::PointerIntPair<RedeclarableTemplateDecl *, 1, bool>
         InstantiatedFromMember;
-
-    /// If non-null, points to an array of specializations (including
-    /// partial specializations) known only by their external declaration IDs.
-    ///
-    /// The first value in the array is the number of specializations/partial
-    /// specializations that follow.
-    LazySpecializationInfo *LazySpecializations = nullptr;
   };
 
   /// Pointer to the common data shared by all declarations of this
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index b045c4f235cef0..e2a165055a442f 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -737,8 +737,10 @@ enum ASTRecordTypes {
 
   /// Record code for updated specialization
   UPDATE_SPECIALIZATION = 73,
-  
+
   CXX_ADDED_TEMPLATE_SPECIALIZATION = 74,
+
+  CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION = 75,
 };
 
 /// Record types used within a source manager block.
@@ -1505,11 +1507,14 @@ enum DeclCode {
   /// A HLSLBufferDecl record.
   DECL_HLSL_BUFFER,
 
+  /// An ImplicitConceptSpecializationDecl record.
+  DECL_IMPLICIT_CONCEPT_SPECIALIZATION,
+
   // A decls specilization record.
   DECL_SPECIALIZATIONS,
 
-  /// An ImplicitConceptSpecializationDecl record.
-  DECL_IMPLICIT_CONCEPT_SPECIALIZATION,
+  // A decls specilization record.
+  DECL_PARTIAL_SPECIALIZATIONS,
 
   DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION
 };
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 12f08386e2329d..fb750175baf2c9 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -635,10 +635,19 @@ class ASTReader
   llvm::DenseMap<const DeclContext *,
                  serialization::reader::DeclContextLookupTable> Lookups;
 
+  using SpecLookupTableTy =
+      llvm::DenseMap<const Decl *,
+                     serialization::reader::LazySpecializationInfoLookupTable>;
   /// Map from decls to specialized decls.
-  llvm::DenseMap<const Decl *,
-                 serialization::reader::LazySpecializationInfoLookupTable>
-      SpecializationsLookups;
+  SpecLookupTableTy SpecializationsLookups;
+  /// Split partial specialization from specialization to speed up lookups.
+  SpecLookupTableTy PartialSpecializationsLookups;
+
+  bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups,
+                                       const Decl *D);
+  bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups,
+                                       const Decl *D,
+                                       ArrayRef<TemplateArgument> TemplateArgs);
 
   // Updates for visible decls can occur for other contexts than just the
   // TU, and when we read those update records, the actual context may not
@@ -655,8 +664,10 @@ class ASTReader
   llvm::DenseMap<GlobalDeclID, DeclContextVisibleUpdates> PendingVisibleUpdates;
 
   using SpecializationsUpdate = SmallVector<UpdateData, 1>;
-  llvm::DenseMap<GlobalDeclID, SpecializationsUpdate>
-      PendingSpecializationsUpdates;
+  using SpecializationsUpdateMap =
+      llvm::DenseMap<GlobalDeclID, SpecializationsUpdate>;
+  SpecializationsUpdateMap PendingSpecializationsUpdates;
+  SpecializationsUpdateMap PendingPartialSpecializationsUpdates;
 
   /// The set of C++ or Objective-C classes that have forward
   /// declarations that have not yet been linked to their definitions.
@@ -691,9 +702,9 @@ class ASTReader
                                      uint64_t Offset, GlobalDeclID ID);
 
   bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor,
-                           uint64_t Offset, Decl *D);
+                           uint64_t Offset, Decl *D, bool IsPartial);
   void AddSpecializations(const Decl *D, const unsigned char *Data,
-                          ModuleFile &M);
+                          ModuleFile &M, bool IsPartial);
 
   /// A vector containing identifiers that have already been
   /// loaded.
@@ -1439,7 +1450,10 @@ class ASTReader
   /// Get the loaded specializations lookup tables for \p D,
   /// if any.
   serialization::reader::LazySpecializationInfoLookupTable *
-  getLoadedSpecializationsLookupTables(const Decl *D);
+  getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial);
+
+  /// If we have any unloaded specialization for \p D
+  bool haveUnloadedSpecializations(const Decl *D) const;
 
 private:
   struct ImportedModule {
diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index 9da22f96130a8b..cdbcc96b9a470e 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -428,6 +428,7 @@ class ASTWriter : public ASTDeserializationListener,
   using SpecializationUpdateMap =
       llvm::MapVector<const NamedDecl *, SmallVector<const Decl *>>;
   SpecializationUpdateMap SpecializationsUpdates;
+  SpecializationUpdateMap PartialSpecializationsUpdates;
 
   using FirstLatestDeclMap = llvm::DenseMap<Decl *, Decl *>;
 
@@ -580,9 +581,10 @@ class ASTWriter : public ASTDeserializationListener,
 
   void GenerateSpecializationInfoLookupTable(
       const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
-      llvm::SmallVectorImpl<char> &LookupTable);
+      llvm::SmallVectorImpl<char> &LookupTable, bool IsPartial);
   uint64_t WriteSpecializationInfoLookupTable(
-      const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations);
+      const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
+      bool IsPartial);
   void GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC,
                                llvm::SmallVectorImpl<char> &LookupTable);
   uint64_t WriteDeclContextLexicalBlock(ASTContext &Context,
@@ -598,7 +600,7 @@ class ASTWriter : public ASTDeserializationListener,
   void WriteDeclAndTypes(ASTContext &Context);
   void PrepareWritingSpecialDecls(Sema &SemaRef);
   void WriteSpecialDeclRecords(Sema &SemaRef);
-  void WriteSpecializationsUpdates();
+  void WriteSpecializationsUpdates(bool IsPartial);
   void WriteDeclUpdatesBlocks(ASTContext &Context,
                               RecordDataImpl &OffsetsRecord);
   void WriteDeclContextVisibleUpdate(ASTContext &Context,
@@ -629,6 +631,7 @@ class ASTWriter : public ASTDeserializationListener,
   unsigned DeclObjCIvarAbbrev = 0;
   unsigned DeclCXXMethodAbbrev = 0;
   unsigned DeclSpecializationsAbbrev = 0;
+  unsigned DeclPartialSpecializationsAbbrev = 0;
 
   unsigned DeclDependentNonTemplateCXXMethodAbbrev = 0;
   unsigned DeclTemplateCXXMethodAbbrev = 0;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index f6f7969314bf54..ce6c879127918d 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1311,13 +1311,11 @@ void LazySpecializationInfoLookupTrait::ReadDataInto(internal_key_type,
   using namespace llvm::support;
 
   for (unsigned NumDecls =
-           DataLen / serialization::reader::LazySpecializationInfo::Length;
+           DataLen / sizeof(serialization::reader::LazySpecializationInfo);
        NumDecls; --NumDecls) {
     LocalDeclID LocalID =
         LocalDeclID::get(Reader, F, endian::readNext<DeclID, llvm::endianness::little, unaligned>(d));
-    const bool IsPartial =
-        endian::readNext<bool, llvm::endianness::little, unaligned>(d);
-    Val.insert({Reader.getGlobalDeclID(F, LocalID), IsPartial});
+    Val.insert(Reader.getGlobalDeclID(F, LocalID));
   }
 }
 
@@ -1410,14 +1408,16 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M,
 }
 
 void ASTReader::AddSpecializations(const Decl *D, const unsigned char *Data,
-                                   ModuleFile &M) {
+                                   ModuleFile &M, bool IsPartial) {
   D = D->getCanonicalDecl();
-  SpecializationsLookups[D].Table.add(
-      &M, Data, reader::LazySpecializationInfoLookupTrait(*this, M));
+  auto &SpecLookups =
+      IsPartial ? PartialSpecializationsLookups : SpecializationsLookups;
+  SpecLookups[D].Table.add(&M, Data,
+                           reader::LazySpecializationInfoLookupTrait(*this, M));
 }
 
 bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor,
-                                    uint64_t Offset, Decl *D) {
+                                    uint64_t Offset, Decl *D, bool IsPartial) {
   assert(Offset != 0);
 
   SavedStreamPosition SavedPosition(Cursor);
@@ -1441,13 +1441,14 @@ bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor,
     return true;
   }
   unsigned RecCode = MaybeRecCode.get();
-  if (RecCode != DECL_SPECIALIZATIONS) {
+  if (RecCode != DECL_SPECIALIZATIONS &&
+      RecCode != DECL_PARTIAL_SPECIALIZATIONS) {
     Error("Expected decl specs block");
     return true;
   }
 
   auto *Data = (const unsigned char *)Blob.data();
-  AddSpecializations(D, Data, M);
+  AddSpecializations(D, Data, M, IsPartial);
   return false;
 }
 
@@ -3556,6 +3557,19 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       break;
     }
 
+    case CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION: {
+      unsigned Idx = 0;
+      GlobalDeclID ID = ReadDeclID(F, Record, Idx);
+      auto *Data = (const unsigned char *)Blob.data();
+      PendingPartialSpecializationsUpdates[ID].push_back(UpdateData{&F, Data});
+      // If we've already loaded the decl, perform the updates when we finish
+      // loading this block.
+      if (Decl *D = GetExistingDecl(ID))
+        PendingUpdateRecords.push_back(
+            PendingUpdateRecord(ID, D, /*JustLoaded=*/false));
+      break;
+    }
+
     case IDENTIFIER_TABLE:
       F.IdentifierTableData =
           reinterpret_cast<const unsigned char *>(Blob.data());
@@ -8136,11 +8150,12 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) {
   return ReadStmtFromStream(*Loc.F);
 }
 
-bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
+bool ASTReader::LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups,
+                                                const Decl *D) {
   assert(D);
 
-  auto It = SpecializationsLookups.find(D);
-  if (It == SpecializationsLookups.end())
+  auto It = SpecLookups.find(D);
+  if (It == SpecLookups.end())
     return false;
 
   // Get Decl may violate the iterator from SpecializationsLookups so we store
@@ -8150,48 +8165,71 @@ bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
 
   // Since we've loaded all the specializations, we can erase it from
   // the lookup table.
-  if (!OnlyPartial)
-    SpecializationsLookups.erase(It);
+  SpecLookups.erase(It);
 
   bool NewSpecsFound = false;
   Deserializing LookupResults(this);
-  for (auto &Info : Infos)
-    if (!OnlyPartial || Info.IsPartial) {
-      if (GetExistingDecl(Info.ID))
-        continue;
-      NewSpecsFound = true;
-      GetDecl(Info.ID);
-    }
+  for (auto &Info : Infos) {
+    if (GetExistingDecl(Info))
+      continue;
+    NewSpecsFound = true;
+    GetDecl(Info);
+  }
 
   return NewSpecsFound;
 }
 
-bool ASTReader::LoadExternalSpecializations(
-    const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
+bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
   assert(D);
 
-  auto It = SpecializationsLookups.find(D);
-  if (It == SpecializationsLookups.end())
+  bool NewSpecsFound =
+      LoadExternalSpecializationsImpl(PartialSpecializationsLookups, D);
+  if (OnlyPartial)
+    return NewSpecsFound;
+
+  NewSpecsFound |= LoadExternalSpecializationsImpl(SpecializationsLookups, D);
+  return NewSpecsFound;
+}
+
+bool ASTReader::LoadExternalSpecializationsImpl(
+    SpecLookupTableTy &SpecLookups, const Decl *D,
+    ArrayRef<TemplateArgument> TemplateArgs) {
+  assert(D);
+
+  auto It = SpecLookups.find(D);
+  if (It == SpecLookups.end())
     return false;
 
   Deserializing LookupResults(this);
   auto HashValue = StableHashForTemplateArguments(TemplateArgs);
 
-  // Get Decl may violate the iterator from SpecializationsLookups
+  // Get Decl may violate the iterator from SpecLookups
   llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos =
       It->second.Table.find(HashValue);
 
   bool NewSpecsFound = false;
   for (auto &Info : Infos) {
-    if (GetExistingDecl(Info.ID))
+    if (GetExistingDecl(Info))
       continue;
     NewSpecsFound = true;
-    GetDecl(Info.ID);
+    GetDecl(Info);
   }
 
   return NewSpecsFound;
 }
 
+bool ASTReader::LoadExternalSpecializations(
+    const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
+  assert(D);
+
+  bool NewDeclsFound = LoadExternalSpecializationsImpl(
+      PartialSpecializationsLookups, D, TemplateArgs);
+  NewDeclsFound |=
+      LoadExternalSpecializationsImpl(SpecializationsLookups, D, TemplateArgs);
+
+  return NewDeclsFound;
+}
+
 void ASTReader::FindExternalLexicalDecls(
     const DeclContext *DC, llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
     SmallVectorImpl<Decl *> &Decls) {
@@ -8372,10 +8410,19 @@ ASTReader::getLoadedLookupTables(DeclContext *Primary) const {
 }
 
 serialization::reader::LazySpecializationInfoLookupTable *
-ASTReader::getLoadedSpecializationsLookupTables(const Decl *D) {
+ASTReader::getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial) {
+  assert(D->isCanonicalDecl());
+  auto &LookupTable =
+      IsPartial ? PartialSpecializationsLookups : SpecializationsLookups;
+  auto I = LookupTable.find(D);
+  return I == LookupTable.end() ? nullptr : &I->second;
+}
+
+bool ASTReader::haveUnloadedSpecializations(const Decl *D) const {
   assert(D->isCanonicalDecl());
-  auto I = SpecializationsLookups.find(D);
-  return I == SpecializationsLookups.end() ? nullptr : &I->second;
+  return (PartialSpecializationsLookups.find(D) !=
+          PartialSpecializationsLookups.end()) ||
+         (SpecializationsLookups.find(D) != SpecializationsLookups.end());
 }
 
 /// Under non-PCH compilation the consumer receives the objc methods
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9ac712d8625db8..bd201e6cb14c11 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -304,7 +304,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
   static void markIncompleteDeclChainImpl(...);
 
   void ReadSpecializations(ModuleFile &M, Decl *D,
-                           llvm::BitstreamCursor &DeclsCursor);
+                           llvm::BitstreamCursor &DeclsCursor, bool IsPartial);
 
   void ReadFunctionDefinition(FunctionDecl *FD);
   void Visit(Decl *D);
@@ -2393,9 +2393,11 @@ void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
 }
 
 void ASTDeclReader::ReadSpecializations(ModuleFile &M, Decl *D,
-                                        llvm::BitstreamCursor &DeclsCursor) {
+                                        llvm::BitstreamCursor &DeclsCursor,
+                                        bool IsPartial) {
   uint64_t Offset = ReadLocalOffset();
-  bool Failed = Reader.ReadSpecializations(M, DeclsCursor, Offset, D);
+  bool Failed =
+      Reader.ReadSpecializations(M, DeclsCursor, Offset, D, IsPartial);
   (void)Failed;
   assert(!Failed);
 }
@@ -2438,7 +2440,8 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false);
+    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/true);
   }
 
   if (D->getTemplatedDecl()->TemplateOrInstantiation) {
@@ -2464,7 +2467,8 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false);
+    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/true);
   }
 }
 
@@ -2563,7 +2567,7 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
 
   if (ThisDeclID == Redecl.getFirstID()) {
     // This FunctionTemplateDecl owns a CommonPtr; read it.
-    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false);
   }
 }
 
@@ -3854,6 +3858,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
   case DECL_CONTEXT_LEXICAL:
   case DECL_CONTEXT_VISIBLE:
   case DECL_SPECIALIZATIONS:
+  case DECL_PARTIAL_SPECIALIZATIONS:
     llvm_unreachable("Record cannot be de-serialized with readDeclRecord");
   case DECL_TYPEDEF:
     D = TypedefDecl::CreateDeserialized(Context, ID);
@@ -4341,7 +4346,17 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
     PendingSpecializationsUpdates.erase(I);
 
     for (const auto &Update : SpecializationUpdates)
-      AddSpecializations(D, Update.Data, *Update.Mod);
+      AddSpecializations(D, Update.Data, *Update.Mod, /*IsPartial=*/false);
+  }
+
+  // Load the pending specializations update for this decl, if it has any.
+  if (auto I = PendingPartialSpecializationsUpdates.find(ID);
+      I != PendingPartialSpecializationsUpdates.end()) {
+    auto SpecializationUpdates = std::move(I->second);
+    PendingPartialSpecializationsUpdates.erase(I);
+
+    for (const auto &Update : SpecializationUpdates)
+      AddSpecializations(D, Update.Data, *Update.Mod, /*IsPartial=*/true);
   }
 }
 
diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h
index 840ddf81e0201a..be0d22d1f4094f 100644
--- a/clang/lib/Serialization/ASTReaderInternals.h
+++ b/clang/lib/Serialization/ASTReaderInternals.h
@@ -119,20 +119,7 @@ struct DeclContextLookupTable {
   MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table;
 };
 
-struct LazySpecializationInfo {
-  // The Decl ID for the specialization.
-  GlobalDeclID ID;
-  // Whether or not this specialization is partial.
-  bool IsPartial;
-
-  bool operator==(const LazySpecializationInfo &Other) const {
-    assert(ID != Other.ID || IsPartial == Other.IsPartial);
-    return ID == Other.ID;
-  }
-  // Records the size record in OnDiskHashTable.
-  // sizeof() may return 8 due to align requirements.
-  static constexpr unsigned Length = sizeof(DeclID) + sizeof(IsPartial);
-};
+using LazySpecializationInfo = GlobalDeclID;
 
 /// Class that performs lookup to specialized decls.
 class LazySpecializationInfoLookupTrait {
@@ -383,32 +370,4 @@ using HeaderFileInfoLookupTable =
 
 } // namespace clang
 
-namespace llvm {
-// ID is unique in LazySpecializationInfo, it is redundant to calculate
-// IsPartial.
-template <>
-struct DenseMapInfo<clang::serialization::reader::LazySpecializationInfo> {
-  using LazySpecializationInfo =
-      clang::serialization::reader::LazySpecializationInfo;
-  using Wrapped = DenseMapInfo<clang::serialization::DeclID>;
-
-  static inline LazySpecializationInfo getEmptyKey() {
-    return {(clang::GlobalDeclID)Wrapped::getEmptyKey(), false};
-  }
-
-  static inline LazySpecializationInfo getTombstoneKey() {
-    return {(clang::GlobalDeclID)Wrapped::getTombstoneKey(), false};
-  }
-
-  static unsigned getHashValue(const LazySpecializationInfo &Key) {
-    return Wrapped::getHashValue(Key.ID.getRawValue());
-  }
-
-  static bool isEqual(const LazySpecializationInfo &LHS,
-                      const LazySpecializationInfo &RHS) {
-    return LHS.ID == RHS.ID;
-  }
-};
-} // end namespace llvm
-
 #endif // LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index d4898b3dac3f52..648096387578bd 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -4129,12 +4129,9 @@ class LazySpecializationInfoLookupTrait {
   template <typename Col, typename Col2> data_type getData(Col &&C, Col2 &ExistingInfo) {
     unsigned Start = Specs.size();
     for (auto *D : C) {
-      bool IsPartial = isa<ClassTemplatePartialSpecializationDecl,
-                           VarTemplatePartialSpecializationDecl>(D);
       NamedDecl *ND = getDeclForLocalLookup(
                            Writer.getLangOpts(), const_cast<NamedDecl *>(D));
-      Specs.push_back({GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()),
-                       IsPartial});
+      Specs.push_back(GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()));
     }
     for (const serialization::reader::LazySpecializationInfo &Info : ExistingInfo)
       Specs.push_back(Info);
@@ -4167,7 +4164,7 @@ class LazySpecializationInfoLookupTrait {
                                                   data_type_ref Lookup) {
     // 4 bytes for each slot.
     unsigned KeyLen = 4;
-    unsigned DataLen = serialization::reader::LazySpecializationInfo::Length *
+    unsigned DataLen = sizeof(serialization::reader::LazySpecializationInfo) *
                        (Lookup.second - Lookup.first);
 
     return emitULEBKeyDataLength(KeyLen, DataLen, Out);
@@ -4188,8 +4185,7 @@ class LazySpecializationInfoLookupTrait {
     uint64_t Start = Out.tell();
     (void)Start;
     for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I) {
-      LE.write<DeclID>(Specs[I].ID.getRawValue());
-      LE.write<bool>(Specs[I].IsPartial);
+      LE.write<DeclID>(Specs[I].getRawValue());
     }
     assert(Out.tell() - Start == DataLen && "Data length is wrong");
   }
@@ -4212,7 +4208,7 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) {
 
 void ASTWriter::GenerateSpecializationInfoLookupTable(
     const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
-    llvm::SmallVectorImpl<char> &LookupTable) {
+    llvm::SmallVectorImpl<char> &LookupTable, bool IsPartial) {
   assert(D->isFirstDecl());
 
   // Create the on-disk hash table representation.
@@ -4238,7 +4234,8 @@ void ASTWriter::GenerateSpecializationInfoLookupTable(
   }
 
   auto *Lookups =
-      Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr;
+      Chain ? Chain->getLoadedSpecializationsLookupTables(D, IsPartial)
+            : nullptr;
 
   for (auto &[HashValue, Specs] : SpecializationMaps) {
     SmallVector<serialization::reader::LazySpecializationInfo, 16> ExisitingSpecs;
@@ -4258,14 +4255,19 @@ void ASTWriter::GenerateSpecializationInfoLookupTable(
 }
 
 uint64_t ASTWriter::WriteSpecializationInfoLookupTable(
-    const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations) {
+    const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
+    bool IsPartial) {
 
   llvm::SmallString<4096> LookupTable;
-  GenerateSpecializationInfoLookupTable(D, Specializations, LookupTable);
+  GenerateSpecializationInfoLookupTable(D, Specializations, LookupTable,
+                                        IsPartial);
 
   uint64_t Offset = Stream.GetCurrentBitNo();
-  RecordData::value_type Record[] = {DECL_SPECIALIZATIONS};
-  Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable);
+  RecordData::value_type Record[] = {IsPartial ? DECL_PARTIAL_SPECIALIZATIONS
+                                               : DECL_SPECIALIZATIONS};
+  Stream.EmitRecordWithBlob(IsPartial ? DeclPartialSpecializationsAbbrev
+                                      : DeclSpecializationsAbbrev,
+                            Record, LookupTable);
 
   return Offset;
 }
@@ -5888,8 +5890,15 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
                       FunctionToLambdaMapAbbrev);
   }
 
-  if (!SpecializationsUpdates.empty())
-    WriteSpecializationsUpdates();
+  if (!SpecializationsUpdates.empty()) {
+    WriteSpecializationsUpdates(/*IsPartial=*/false);
+    SpecializationsUpdates.clear();
+  }
+
+  if (!PartialSpecializationsUpdates.empty()) {
+    WriteSpecializationsUpdates(/*IsPartial=*/true);
+    PartialSpecializationsUpdates.clear();
+  }
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
   // Create a lexical update block containing all of the declarations in the
@@ -5934,23 +5943,27 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
     WriteDeclContextVisibleUpdate(Context, DC);
 }
 
-void ASTWriter::WriteSpecializationsUpdates() {
+void ASTWriter::WriteSpecializationsUpdates(bool IsPartial) {
+  auto RecordType = IsPartial ? CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION
+                              : CXX_ADDED_TEMPLATE_SPECIALIZATION;
+
   auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
-  Abv->Add(llvm::BitCodeAbbrevOp(CXX_ADDED_TEMPLATE_SPECIALIZATION));
+  Abv->Add(llvm::BitCodeAbbrevOp(RecordType));
   Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
   Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
   auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv));
 
-  for (auto &SpecializationUpdate : SpecializationsUpdates) {
+  auto &SpecUpdates =
+      IsPartial ? PartialSpecializationsUpdates : SpecializationsUpdates;
+  for (auto &SpecializationUpdate : SpecUpdates) {
     const NamedDecl *D = SpecializationUpdate.first;
 
     llvm::SmallString<4096> LookupTable;
     GenerateSpecializationInfoLookupTable(D, SpecializationUpdate.second,
-                                          LookupTable);
+                                          LookupTable, IsPartial);
 
     // Write the lookup table
-    RecordData::value_type Record[] = {CXX_ADDED_TEMPLATE_SPECIALIZATION,
-                                       getDeclID(D).getRawValue()};
+    RecordData::value_type Record[] = {RecordType, getDeclID(D).getRawValue()};
     Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable);
   }
 }
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 78c4f3bd62c33f..b3119607a14043 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -208,15 +208,21 @@ namespace clang {
     /// ODRHash of the template arguments of D which should provide enough
     /// information to load D only if the template instantiator needs it.
     void AddFirstSpecializationDeclFromEachModule(
-        const Decl *D, llvm::SmallVectorImpl<const Decl *> &SpecsInMap) {
+        const Decl *D, llvm::SmallVectorImpl<const Decl *> &SpecsInMap,
+        llvm::SmallVectorImpl<const Decl *> &PartialSpecsInMap) {
       assert((isa<ClassTemplateSpecializationDecl>(D) ||
               isa<VarTemplateSpecializationDecl>(D) || isa<FunctionDecl>(D)) &&
              "Must not be called with other decls");
       llvm::MapVector<ModuleFile *, const Decl *> Firsts;
       CollectFirstDeclFromEachModule(D, /*IncludeLocal*/ true, Firsts);
 
-      for (const auto &F : Firsts)
-        SpecsInMap.push_back(F.second);
+      for (const auto &F : Firsts) {
+        if (isa<ClassTemplatePartialSpecializationDecl,
+                VarTemplatePartialSpecializationDecl>(F.second))
+          PartialSpecsInMap.push_back(F.second);
+        else
+          SpecsInMap.push_back(F.second);
+      }
     }
 
     /// Get the specialization decl from an entry in the specialization list.
@@ -243,29 +249,38 @@ namespace clang {
       // If we have any lazy specializations, and the external AST source is
       // our chained AST reader, we can just write out the DeclIDs. Otherwise,
       // we need to resolve them to actual declarations.
-      if (Writer.Chain != Writer.Context->getExternalSource() && Writer.Chain &&
-          Writer.Chain->getLoadedSpecializationsLookupTables(D)) {
+      if (Writer.Chain != Record.getASTContext().getExternalSource() &&
+          Writer.Chain && Writer.Chain->haveUnloadedSpecializations(D)) {
         D->LoadLazySpecializations();
-        assert(!Writer.Chain->getLoadedSpecializationsLookupTables(D));
+        assert(!Writer.Chain->haveUnloadedSpecializations(D));
       }
 
-      // AddFirstDeclFromEachModule might trigger deserialization, invalidating
-      // *Specializations iterators.
-      llvm::SmallVector<const Decl*, 16> Specs;
+      // AddFirstSpecializationDeclFromEachModule might trigger deserialization,
+      // invalidating *Specializations iterators.
+      llvm::SmallVector<const Decl *, 16> AllSpecs;
       for (auto &Entry : Common->Specializations)
-        Specs.push_back(getSpecializationDecl(Entry));
+        AllSpecs.push_back(getSpecializationDecl(Entry));
       for (auto &Entry : getPartialSpecializations(Common))
-        Specs.push_back(getSpecializationDecl(Entry));
-
-      llvm::SmallVector<const Decl *, 16> SpecsInOnDiskMap = Specs;
+        AllSpecs.push_back(getSpecializationDecl(Entry));
 
-      for (auto *D : Specs) {
+      llvm::SmallVector<const Decl *, 16> Specs;
+      llvm::SmallVector<const Decl *, 16> PartialSpecs;
+      for (auto *D : AllSpecs) {
         assert(D->isCanonicalDecl() && "non-canonical decl in set");
-        AddFirstSpecializationDeclFromEachModule(D, SpecsInOnDiskMap);
+        AddFirstSpecializationDeclFromEachModule(D, Specs, PartialSpecs);
       }
 
-      Record.AddOffset(
-          Writer.WriteSpecializationInfoLookupTable(D, SpecsInOnDiskMap));
+      Record.AddOffset(Writer.WriteSpecializationInfoLookupTable(
+          D, Specs, /*IsPartial=*/false));
+
+      // Function Template Decl doesn't have partial decls.
+      if (isa<FunctionTemplateDecl>(D)) {
+        assert(PartialSpecs.empty());
+        return;
+      }
+
+      Record.AddOffset(Writer.WriteSpecializationInfoLookupTable(
+          D, PartialSpecs, /*IsPartial=*/true));
     }
 
     /// Ensure that this template specialization is associated with the specified
@@ -286,8 +301,13 @@ namespace clang {
       if (Writer.getFirstLocalDecl(Specialization) != Specialization)
         return;
 
-      Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back(
-          cast<NamedDecl>(Specialization));
+      if (isa<ClassTemplatePartialSpecializationDecl,
+              VarTemplatePartialSpecializationDecl>(Specialization))
+        Writer.PartialSpecializationsUpdates[cast<NamedDecl>(Template)]
+            .push_back(cast<NamedDecl>(Specialization));
+      else
+        Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back(
+            cast<NamedDecl>(Specialization));
     }
   };
 }
@@ -2797,6 +2817,11 @@ void ASTWriter::WriteDeclAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(serialization::DECL_SPECIALIZATIONS));
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
   DeclSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv));
+
+  Abv = std::make_shared<BitCodeAbbrev>();
+  Abv->Add(BitCodeAbbrevOp(serialization::DECL_PARTIAL_SPECIALIZATIONS));
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+  DeclPartialSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv));
 }
 
 /// isRequiredDecl - Check if this is a "required" Decl, which must be seen by



More information about the llvm-branch-commits mailing list