https://github.com/aviralg updated 
https://github.com/llvm/llvm-project/pull/205946

>From c34103c3b0409651407febfe2fe84f1b2a7e60c8 Mon Sep 17 00:00:00 2001
From: Aviral Goel <[email protected]>
Date: Thu, 25 Jun 2026 16:05:31 -0700
Subject: [PATCH 1/4] Add static library

---
 .../Core/EntityLinker/StaticLibrary.h         |  73 +++++++
 .../Core/EntityLinker/TUSummaryEncoding.h     |  27 +++
 .../Core/Model/BuildNamespace.h               |   6 +-
 .../Core/Model/PrivateFieldNames.def          |   3 +
 .../Core/Serialization/JSONFormat.h           |  15 ++
 .../Core/Serialization/SerializationFormat.h  |  14 +-
 .../Core/CMakeLists.txt                       |   1 +
 .../Core/ModelStringConversions.h             |   4 +
 .../Serialization/JSONFormat/Artifact.cpp     |  19 +-
 .../Serialization/JSONFormat/JSONFormatImpl.h |   5 +-
 .../JSONFormat/StaticLibrary.cpp              | 192 ++++++++++++++++++
 .../JSONFormat/TUSummaryEncoding.cpp          |  19 +-
 .../Inputs/rt-static-library-empty.json       |   9 +
 .../ssaf-format/Artifact/round-trip.test      |   9 +
 .../ssaf-format/Artifact/top-level.test       |   2 +-
 .../Inputs/duplicate-member.json              |  32 +++
 .../Inputs/member-mismatched-type.json        |  18 ++
 .../Inputs/member-not-object.json             |   9 +
 .../Inputs/member-triple-mismatch.json        |  21 ++
 .../Inputs/members-not-array.json             |   9 +
 .../StaticLibrary/Inputs/mismatched-type.json |   9 +
 .../StaticLibrary/Inputs/missing-members.json |   8 +
 .../Inputs/missing-namespace.json             |   5 +
 .../Inputs/missing-target-triple.json         |   8 +
 .../StaticLibrary/Inputs/missing-type.json    |   8 +
 .../Inputs/namespace-invalid-kind.json        |   9 +
 .../Inputs/namespace-missing-kind.json        |   8 +
 .../Inputs/namespace-missing-name.json        |   8 +
 .../Inputs/namespace-wrong-kind.json          |   9 +
 .../Inputs/not-normalized-target-triple.json  |   9 +
 .../StaticLibrary/Inputs/rt-empty.json        |   9 +
 .../Inputs/rt-multiple-members.json           |  43 ++++
 .../Inputs/rt-single-member.json              |  21 ++
 .../ssaf-format/StaticLibrary/round-trip.test |  28 +++
 .../ssaf-format/StaticLibrary/top-level.test  | 109 ++++++++++
 clang/tools/clang-ssaf-format/SSAFFormat.cpp  |  11 +-
 .../BuildNamespaceTest.cpp                    |   8 +
 .../ScalableStaticAnalysis/CMakeLists.txt     |   1 +
 .../TUSummaryExtractorFrontendActionTest.cpp  |  17 ++
 .../ModelStringConversionsTest.cpp            |  14 ++
 .../Registries/MockSerializationFormat.cpp    |  11 +
 .../Registries/MockSerializationFormat.h      |   6 +
 .../StaticLibraryTest.cpp                     |  33 +++
 .../ScalableStaticAnalysis/TestFixture.h      |   1 +
 44 files changed, 865 insertions(+), 15 deletions(-)
 create mode 100644 
clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
 create mode 100644 
clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test
 create mode 100644 clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp

diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
new file mode 100644
index 0000000000000..9027a603722c5
--- /dev/null
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
@@ -0,0 +1,73 @@
+//===- StaticLibrary.h ------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the StaticLibrary class, which represents a static
+// library of translation unit summary encodings (the SSAF analogue of an
+// ar / libtool -static / lib.exe output).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H
+#define LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H
+
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
+#include "llvm/TargetParser/Triple.h"
+#include <memory>
+#include <set>
+
+namespace clang::ssaf {
+
+/// Represents a static library of translation unit summary encodings.
+///
+/// A StaticLibrary bundles member translation units without performing
+/// entity resolution, mirroring the role of ar / libtool -static / lib.exe
+/// in native build pipelines. The final linker is responsible for
+/// selective inclusion when a StaticLibrary appears on its command line.
+///
+/// Static libraries are single-architecture: every member's target triple
+/// must equal the library's. Multi-architecture static libraries are
+/// expressed as a fat wrapper around per-architecture StaticLibrary
+/// instances rather than as a single mixed-architecture library.
+///
+/// Members are stored as encoded TUSummaryEncoding objects: the archiver
+/// tool never decodes per-entity payloads, and the linker consumes them
+/// as-is during its selective inclusion pass.
+class StaticLibrary {
+  friend class EntityLinker;
+  friend class SerializationFormat;
+  friend class TestFixture;
+
+  // Target triple of the static library. All member TUs must share this
+  // triple.
+  llvm::Triple TargetTriple;
+
+  // The namespace identifying this static library (kind=StaticLibrary).
+  BuildNamespace Namespace;
+
+  // Member translation units, ordered by their TUNamespace. Membership is
+  // by namespace identity: inserting a TU whose TUNamespace already exists
+  // in the set is rejected during deserialization. The comparator reaches
+  // the members' private TUNamespace field via friend access on
+  // TUSummaryEncoding rather than through a public accessor.
+  std::set<std::unique_ptr<TUSummaryEncoding>, TUSummaryEncodingByNamespace>
+      Members;
+
+public:
+  StaticLibrary(llvm::Triple TargetTriple, BuildNamespace Namespace)
+      : TargetTriple(std::move(TargetTriple)), Namespace(std::move(Namespace)) 
{
+  }
+
+  const llvm::Triple &getTargetTriple() const { return TargetTriple; }
+
+  const BuildNamespace &getNamespace() const { return Namespace; }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
index 2c672a55e4873..6936bbc8fcffc 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
@@ -26,6 +26,8 @@
 
 namespace clang::ssaf {
 
+struct TUSummaryEncodingByNamespace;
+
 /// Represents a translation unit summary in its serialized encoding.
 ///
 /// TUSummaryEncoding holds entity summary data in a format-specific encoding
@@ -36,6 +38,7 @@ class TUSummaryEncoding {
   friend class EntityLinker;
   friend class SerializationFormat;
   friend class TestFixture;
+  friend struct TUSummaryEncodingByNamespace;
 
   // Target triple of the translation unit.
   llvm::Triple TargetTriple;
@@ -62,6 +65,30 @@ class TUSummaryEncoding {
   const llvm::Triple &getTargetTriple() const { return TargetTriple; }
 };
 
+/// Comparator that orders TUSummaryEncoding pointers by their TUNamespace.
+/// Lives here (next to TUSummaryEncoding) rather than inside StaticLibrary
+/// so it can reach the private TUNamespace field via friend access without
+/// exposing a public accessor. The transparent overloads enable
+/// std::set::find() lookup by a bare BuildNamespace.
+struct TUSummaryEncodingByNamespace {
+  using is_transparent = void;
+
+  bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
+                  const std::unique_ptr<TUSummaryEncoding> &B) const {
+    return A->TUNamespace < B->TUNamespace;
+  }
+
+  bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
+                  const BuildNamespace &B) const {
+    return A->TUNamespace < B;
+  }
+
+  bool operator()(const BuildNamespace &A,
+                  const std::unique_ptr<TUSummaryEncoding> &B) const {
+    return A < B->TUNamespace;
+  }
+};
+
 } // namespace clang::ssaf
 
 #endif // 
LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_TUSUMMARYENCODING_H
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h
index 6da5a9e42c4da..1549b357ea50d 100644
--- a/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h
+++ b/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h
@@ -27,7 +27,11 @@
 
 namespace clang::ssaf {
 
-enum class BuildNamespaceKind : unsigned short { CompilationUnit, LinkUnit };
+enum class BuildNamespaceKind : unsigned short {
+  CompilationUnit,
+  LinkUnit,
+  StaticLibrary
+};
 
 /// Represents a single namespace in the build process.
 ///
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def 
b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
index 44931026ba428..6354ebd06ae41 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
@@ -19,6 +19,9 @@
 
 FIELD(BuildNamespace, Kind)
 FIELD(BuildNamespace, Name)
+FIELD(StaticLibrary, TargetTriple)
+FIELD(StaticLibrary, Namespace)
+FIELD(StaticLibrary, Members)
 FIELD(EntityId, Index)
 FIELD(EntityIdTable, Entities)
 FIELD(EntityLinkage, Linkage)
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h
index 9d9b1ff0bbb51..d4b6cfc0e1690 100644
--- a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h
@@ -68,6 +68,12 @@ class JSONFormat final : public SerializationFormat {
   llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
                                      llvm::StringRef Path) override;
 
+  llvm::Expected<StaticLibrary>
+  readStaticLibrary(llvm::StringRef Path) override;
+
+  llvm::Error writeStaticLibrary(const StaticLibrary &S,
+                                 llvm::StringRef Path) override;
+
   llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override;
 
   llvm::Error writeWPASuite(const WPASuite &Suite,
@@ -122,6 +128,15 @@ class JSONFormat final : public SerializationFormat {
   llvm::Expected<LUSummaryEncoding>
   readLUSummaryEncodingFromObject(const Object &Root);
 
+  /// Parses a StaticLibrary from an already-validated root JSON object.
+  /// See \c readTUSummaryFromObject for caller responsibilities.
+  llvm::Expected<StaticLibrary> readStaticLibraryFromObject(const Object 
&Root);
+
+  /// Serializes a TUSummaryEncoding to a JSON object including its
+  /// self-describing \c type field. Used both by \c writeTUSummaryEncoding
+  /// and by the StaticLibrary writer to emit member entries.
+  Object tuSummaryEncodingToJSON(const TUSummaryEncoding &SE) const;
+
   /// Parses a WPASuite from an already-validated root JSON object. See
   /// \c readTUSummaryFromObject for caller responsibilities.
   llvm::Expected<WPASuite> readWPASuiteFromObject(const Object &Root);
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h
 
b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h
index ca3ee9ce3362c..29b662bc0e380 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h
@@ -16,6 +16,7 @@
 
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/SummaryName.h"
@@ -40,7 +41,12 @@ using Artifact = std::variant<TUSummary, LUSummary, 
WPASuite>;
 /// Lazily-deserialized counterpart of \c Artifact: the same on-disk
 /// artifacts but with their per-entity summary payloads left as opaque
 /// format-specific encodings rather than fully resolved analysis results.
-using ArtifactEncoding = std::variant<TUSummaryEncoding, LUSummaryEncoding>;
+///
+/// \c StaticLibrary appears only in this variant: the archiver tool and
+/// the linker pass member payloads through without decoding them, so a
+/// fully decoded static-library shape would have no consumer.
+using ArtifactEncoding =
+    std::variant<TUSummaryEncoding, LUSummaryEncoding, StaticLibrary>;
 
 /// Abstract base class for serialization formats.
 class SerializationFormat {
@@ -94,6 +100,12 @@ class SerializationFormat {
   writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
                          llvm::StringRef Path) = 0;
 
+  virtual llvm::Expected<StaticLibrary>
+  readStaticLibrary(llvm::StringRef Path) = 0;
+
+  virtual llvm::Error writeStaticLibrary(const StaticLibrary &S,
+                                         llvm::StringRef Path) = 0;
+
   virtual llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) = 0;
 
   virtual llvm::Error writeWPASuite(const WPASuite &Suite,
diff --git a/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt 
b/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt
index b2cde5f225445..0a0ce19d63732 100644
--- a/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt
@@ -17,6 +17,7 @@ add_clang_library(clangScalableStaticAnalysisCore
   Serialization/JSONFormat/JSONFormatImpl.cpp
   Serialization/JSONFormat/LUSummary.cpp
   Serialization/JSONFormat/LUSummaryEncoding.cpp
+  Serialization/JSONFormat/StaticLibrary.cpp
   Serialization/JSONFormat/TUSummary.cpp
   Serialization/JSONFormat/TUSummaryEncoding.cpp
   Serialization/JSONFormat/WPASuite.cpp
diff --git a/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h 
b/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h
index ee1603e50025f..8d3c932205740 100644
--- a/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h
+++ b/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h
@@ -36,6 +36,8 @@ inline llvm::StringRef 
buildNamespaceKindToString(BuildNamespaceKind BNK) {
     return "CompilationUnit";
   case BuildNamespaceKind::LinkUnit:
     return "LinkUnit";
+  case BuildNamespaceKind::StaticLibrary:
+    return "StaticLibrary";
   }
   llvm_unreachable("Unhandled BuildNamespaceKind variant");
 }
@@ -48,6 +50,8 @@ buildNamespaceKindFromString(llvm::StringRef Str) {
     return BuildNamespaceKind::CompilationUnit;
   if (Str == "LinkUnit")
     return BuildNamespaceKind::LinkUnit;
+  if (Str == "StaticLibrary")
+    return BuildNamespaceKind::StaticLibrary;
   return std::nullopt;
 }
 
diff --git 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp
index 424d7368cf33f..e5fc46a8ea952 100644
--- 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp
+++ 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp
@@ -145,10 +145,21 @@ JSONFormat::readArtifactEncoding(llvm::StringRef Path) {
     return ArtifactEncoding{std::move(*ExpectedLU)};
   }
 
+  if (*ExpectedType == JSONTypeValueStaticLibrary) {
+    auto ExpectedStaticLibrary = readStaticLibraryFromObject(*RootObjectPtr);
+    if (!ExpectedStaticLibrary) {
+      return ErrorBuilder::wrap(ExpectedStaticLibrary.takeError())
+          .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path)
+          .build();
+    }
+    return ArtifactEncoding{std::move(*ExpectedStaticLibrary)};
+  }
+
   return ErrorBuilder::create(std::errc::invalid_argument,
                               ErrorMessages::UnknownArtifactEncodingType,
                               *ExpectedType, JSONTypeKey,
-                              JSONTypeValueTUSummary, JSONTypeValueLUSummary)
+                              JSONTypeValueTUSummary, JSONTypeValueLUSummary,
+                              JSONTypeValueStaticLibrary)
       .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path)
       .build();
 }
@@ -160,11 +171,13 @@ llvm::Error JSONFormat::writeArtifactEncoding(const 
ArtifactEncoding &E,
         using T = std::decay_t<decltype(Enc)>;
         if constexpr (std::is_same_v<T, TUSummaryEncoding>) {
           return writeTUSummaryEncoding(Enc, Path);
+        } else if constexpr (std::is_same_v<T, LUSummaryEncoding>) {
+          return writeLUSummaryEncoding(Enc, Path);
         } else {
           static_assert(
-              std::is_same_v<T, LUSummaryEncoding>,
+              std::is_same_v<T, StaticLibrary>,
               "ArtifactEncoding visitor must cover all variant alternatives");
-          return writeLUSummaryEncoding(Enc, Path);
+          return writeStaticLibrary(Enc, Path);
         }
       },
       E);
diff --git 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h
 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h
index 191be83ccb0e5..e83a8dabac113 100644
--- 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h
+++ 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h
@@ -85,7 +85,7 @@ inline constexpr const char *MismatchedSummaryType =
 inline constexpr const char *UnknownArtifactType =
     "unknown value '{0}' for field '{1}': expected '{2}', '{3}', or '{4}'";
 inline constexpr const char *UnknownArtifactEncodingType =
-    "unknown value '{0}' for field '{1}': expected '{2}', or '{3}'";
+    "unknown value '{0}' for field '{1}': expected '{2}', '{3}', or '{4}'";
 
 inline constexpr const char *FailedToDeserializeEntitySummaryNoFormatInfo =
     "failed to deserialize EntitySummary: no FormatInfo registered for '{0}'";
@@ -149,6 +149,9 @@ inline constexpr const char *JSONTypeValueTUSummary = 
"TUSummary";
 /// Value written to \c JSONTypeKey for serialized \c LUSummary files.
 inline constexpr const char *JSONTypeValueLUSummary = "LUSummary";
 
+/// Value written to \c JSONTypeKey for serialized \c StaticLibrary files.
+inline constexpr const char *JSONTypeValueStaticLibrary = "StaticLibrary";
+
 /// Value written to \c JSONTypeKey for serialized \c WPASuite files.
 inline constexpr const char *JSONTypeValueWPASuite = "WPASuite";
 
diff --git 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp
 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp
new file mode 100644
index 0000000000000..b34c63b185b0e
--- /dev/null
+++ 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp
@@ -0,0 +1,192 @@
+//===- StaticLibrary.cpp 
--------------------------------------------------===//
+//
+// 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 "JSONFormatImpl.h"
+
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace clang::ssaf {
+
+//----------------------------------------------------------------------------
+// StaticLibrary
+//----------------------------------------------------------------------------
+
+llvm::Expected<StaticLibrary>
+JSONFormat::readStaticLibrary(llvm::StringRef Path) {
+  auto ExpectedJSON = readJSON(Path);
+  if (!ExpectedJSON) {
+    return ErrorBuilder::wrap(ExpectedJSON.takeError())
+        .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path)
+        .build();
+  }
+
+  Object *RootObjectPtr = ExpectedJSON->getAsObject();
+  if (!RootObjectPtr) {
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                ErrorMessages::FailedToReadObject,
+                                "StaticLibrary", "object")
+        .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path)
+        .build();
+  }
+
+  if (auto Err = checkSummaryType(*RootObjectPtr, JSONTypeValueStaticLibrary)) 
{
+    return ErrorBuilder::wrap(std::move(Err))
+        .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path)
+        .build();
+  }
+
+  auto ExpectedStaticLibrary = readStaticLibraryFromObject(*RootObjectPtr);
+  if (!ExpectedStaticLibrary) {
+    return ErrorBuilder::wrap(ExpectedStaticLibrary.takeError())
+        .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path)
+        .build();
+  }
+
+  return std::move(*ExpectedStaticLibrary);
+}
+
+llvm::Expected<StaticLibrary>
+JSONFormat::readStaticLibraryFromObject(const Object &RootObject) {
+  auto OptTargetTriple = RootObject.getString("target_triple");
+  if (!OptTargetTriple) {
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                ErrorMessages::FailedToReadObjectAtField,
+                                "TargetTriple", "target_triple", "string")
+        .build();
+  }
+
+  if (auto Err = validateNormalizedTargetTriple(*OptTargetTriple)) {
+    return ErrorBuilder::wrap(std::move(Err))
+        .context(ErrorMessages::ReadingFromField, "TargetTriple",
+                 "target_triple")
+        .build();
+  }
+
+  llvm::Triple T(*OptTargetTriple);
+
+  const Object *NamespaceObject = RootObject.getObject("namespace");
+  if (!NamespaceObject) {
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                ErrorMessages::FailedToReadObjectAtField,
+                                "BuildNamespace", "namespace", "object")
+        .build();
+  }
+
+  auto ExpectedNamespace = buildNamespaceFromJSON(*NamespaceObject);
+  if (!ExpectedNamespace) {
+    return ErrorBuilder::wrap(ExpectedNamespace.takeError())
+        .context(ErrorMessages::ReadingFromField, "BuildNamespace", 
"namespace")
+        .build();
+  }
+
+  if (getKind(*ExpectedNamespace) != BuildNamespaceKind::StaticLibrary) {
+    return ErrorBuilder::create(
+               std::errc::invalid_argument,
+               ErrorMessages::MismatchedSummaryType,
+               buildNamespaceKindToJSON(BuildNamespaceKind::StaticLibrary),
+               "namespace.kind",
+               buildNamespaceKindToJSON(getKind(*ExpectedNamespace)))
+        .build();
+  }
+
+  StaticLibrary S(std::move(T), std::move(*ExpectedNamespace));
+
+  const Array *MembersArray = RootObject.getArray("members");
+  if (!MembersArray) {
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                ErrorMessages::FailedToReadObjectAtField,
+                                "StaticLibrary members", "members", "array")
+        .build();
+  }
+
+  auto &Members = getMembers(S);
+  const auto &StaticLibraryTriple = getTargetTriple(S);
+
+  for (const auto &[Index, MemberValue] : llvm::enumerate(*MembersArray)) {
+    const Object *MemberObject = MemberValue.getAsObject();
+    if (!MemberObject) {
+      return ErrorBuilder::create(std::errc::invalid_argument,
+                                  ErrorMessages::FailedToReadObjectAtIndex,
+                                  "StaticLibrary member", Index, "object")
+          .build();
+    }
+
+    if (auto Err = checkSummaryType(*MemberObject, JSONTypeValueTUSummary)) {
+      return ErrorBuilder::wrap(std::move(Err))
+          .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member",
+                   Index)
+          .build();
+    }
+
+    auto ExpectedMember = readTUSummaryEncodingFromObject(*MemberObject);
+    if (!ExpectedMember) {
+      return ErrorBuilder::wrap(ExpectedMember.takeError())
+          .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member",
+                   Index)
+          .build();
+    }
+
+    if (ExpectedMember->getTargetTriple() != StaticLibraryTriple) {
+      return ErrorBuilder::create(
+                 std::errc::invalid_argument,
+                 ErrorMessages::MismatchedSummaryType,
+                 llvm::Triple::normalize(StaticLibraryTriple.str()),
+                 "target_triple",
+                 llvm::Triple::normalize(
+                     ExpectedMember->getTargetTriple().str()))
+          .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member",
+                   Index)
+          .build();
+    }
+
+    auto MemberNamespace = getTUNamespace(*ExpectedMember);
+    auto Owned =
+        std::make_unique<TUSummaryEncoding>(std::move(*ExpectedMember));
+    auto [It, Inserted] = Members.insert(std::move(Owned));
+    if (!Inserted) {
+      return ErrorBuilder::create(std::errc::invalid_argument,
+                                  ErrorMessages::FailedInsertionOnDuplication,
+                                  "StaticLibrary member", Index,
+                                  MemberNamespace)
+          .build();
+    }
+  }
+
+  return std::move(S);
+}
+
+llvm::Error JSONFormat::writeStaticLibrary(const StaticLibrary &S,
+                                           llvm::StringRef Path) {
+  Object RootObject;
+
+  RootObject[JSONTypeKey] = JSONTypeValueStaticLibrary;
+
+  RootObject["target_triple"] =
+      llvm::Triple::normalize(getTargetTriple(S).str());
+
+  RootObject["namespace"] = buildNamespaceToJSON(getNamespace(S));
+
+  Array MembersArray;
+  MembersArray.reserve(getMembers(S).size());
+  for (const auto &Member : getMembers(S)) {
+    MembersArray.push_back(tuSummaryEncodingToJSON(*Member));
+  }
+  RootObject["members"] = std::move(MembersArray);
+
+  if (auto Error = writeJSON(std::move(RootObject), Path)) {
+    return ErrorBuilder::wrap(std::move(Error))
+        .context(ErrorMessages::WritingToFile, "StaticLibrary", Path)
+        .build();
+  }
+
+  return llvm::Error::success();
+}
+
+} // namespace clang::ssaf
diff --git 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp
 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp
index 5a5ab98731c47..3ee4f059bd458 100644
--- 
a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp
+++ 
b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp
@@ -164,6 +164,17 @@ JSONFormat::readTUSummaryEncodingFromObject(const Object 
&RootObject) {
 llvm::Error
 JSONFormat::writeTUSummaryEncoding(const TUSummaryEncoding &SummaryEncoding,
                                    llvm::StringRef Path) {
+  if (auto Error = writeJSON(tuSummaryEncodingToJSON(SummaryEncoding), Path)) {
+    return ErrorBuilder::wrap(std::move(Error))
+        .context(ErrorMessages::WritingToFile, "TUSummary", Path)
+        .build();
+  }
+
+  return llvm::Error::success();
+}
+
+Object JSONFormat::tuSummaryEncodingToJSON(
+    const TUSummaryEncoding &SummaryEncoding) const {
   Object RootObject;
 
   RootObject[JSONTypeKey] = JSONTypeValueTUSummary;
@@ -181,13 +192,7 @@ JSONFormat::writeTUSummaryEncoding(const TUSummaryEncoding 
&SummaryEncoding,
 
   RootObject["data"] = encodingSummaryDataMapToJSON(getData(SummaryEncoding));
 
-  if (auto Error = writeJSON(std::move(RootObject), Path)) {
-    return ErrorBuilder::wrap(std::move(Error))
-        .context(ErrorMessages::WritingToFile, "TUSummary", Path)
-        .build();
-  }
-
-  return llvm::Error::success();
+  return RootObject;
 }
 
 } // namespace clang::ssaf
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json
 
b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json
new file mode 100644
index 0000000000000..3059d007a2e9e
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json
@@ -0,0 +1,9 @@
+{
+  "members": [],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libempty"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test 
b/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test
index 4ddb233940afe..d6d67826e4847 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test
@@ -38,3 +38,12 @@
 // RUN: diff %S/Inputs/rt-lu-empty.json %t/rt-lu-empty-enc.json
 // RUN: clang-ssaf-format --type auto --encoding %S/Inputs/rt-lu-empty.json -o 
%t/rt-lu-empty-enc-explicit.json
 // RUN: diff %S/Inputs/rt-lu-empty.json %t/rt-lu-empty-enc-explicit.json
+
+// StaticLibrary appears only in the ArtifactEncoding variant (the archiver
+// tool and the linker pass member payloads through encoded), so it
+// round-trips only via --encoding here.
+
+// RUN: clang-ssaf-format --encoding %S/Inputs/rt-static-library-empty.json -o 
%t/rt-static-library-empty-enc.json
+// RUN: diff %S/Inputs/rt-static-library-empty.json 
%t/rt-static-library-empty-enc.json
+// RUN: clang-ssaf-format --type auto --encoding 
%S/Inputs/rt-static-library-empty.json -o 
%t/rt-static-library-empty-enc-explicit.json
+// RUN: diff %S/Inputs/rt-static-library-empty.json 
%t/rt-static-library-empty-enc-explicit.json
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test 
b/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test
index 904b1dffa7d7b..7838f3a90534c 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test
@@ -40,4 +40,4 @@
 // RUN: not clang-ssaf-format --type auto --encoding 
%S/Inputs/unknown-type.json 2>&1 \
 // RUN:   | FileCheck %s --match-full-lines 
--check-prefix=ENCODING-UNKNOWN-TYPE
 // ENCODING-UNKNOWN-TYPE:      clang-ssaf-format: error: reading 
ArtifactEncoding from file '{{.*}}unknown-type.json'
-// ENCODING-UNKNOWN-TYPE-NEXT: unknown value 'Mystery' for field 'type': 
expected 'TUSummary', or 'LUSummary'
+// ENCODING-UNKNOWN-TYPE-NEXT: unknown value 'Mystery' for field 'type': 
expected 'TUSummary', 'LUSummary', or 'StaticLibrary'
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json
new file mode 100644
index 0000000000000..e3ea75ba7024c
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json
@@ -0,0 +1,32 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": [
+    {
+      "type": "TUSummary",
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "a.cpp"
+      },
+      "id_table": [],
+      "linkage_table": [],
+      "data": []
+    },
+    {
+      "type": "TUSummary",
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "a.cpp"
+      },
+      "id_table": [],
+      "linkage_table": [],
+      "data": []
+    }
+  ]
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json
new file mode 100644
index 0000000000000..cf85ec21d0d4e
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json
@@ -0,0 +1,18 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": [
+    {
+      "type": "LUSummary",
+      "target_triple": "arm64-apple-macosx",
+      "lu_namespace": [],
+      "id_table": [],
+      "linkage_table": [],
+      "data": []
+    }
+  ]
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json
new file mode 100644
index 0000000000000..d62af500c4b04
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json
@@ -0,0 +1,9 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": ["not an object"]
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json
new file mode 100644
index 0000000000000..39a8c23970d1d
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json
@@ -0,0 +1,21 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": [
+    {
+      "type": "TUSummary",
+      "target_triple": "x86_64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "a.cpp"
+      },
+      "id_table": [],
+      "linkage_table": [],
+      "data": []
+    }
+  ]
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json
new file mode 100644
index 0000000000000..1686e7ac517b4
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json
@@ -0,0 +1,9 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": {}
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json
new file mode 100644
index 0000000000000..5c754996b18ce
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json
@@ -0,0 +1,9 @@
+{
+  "type": "TUSummary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json
new file mode 100644
index 0000000000000..6f5059edaac85
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json
@@ -0,0 +1,8 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  }
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json
new file mode 100644
index 0000000000000..59aa554be123c
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json
@@ -0,0 +1,5 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json
new file mode 100644
index 0000000000000..6ab76fc69aa0c
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json
@@ -0,0 +1,8 @@
+{
+  "type": "StaticLibrary",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json
new file mode 100644
index 0000000000000..9e470063d7865
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json
@@ -0,0 +1,8 @@
+{
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json
new file mode 100644
index 0000000000000..5e96399f34387
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json
@@ -0,0 +1,9 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "invalid_kind",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json
new file mode 100644
index 0000000000000..7aa0a00cdc4a2
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json
@@ -0,0 +1,8 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json
new file mode 100644
index 0000000000000..750e310763ee9
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json
@@ -0,0 +1,8 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "StaticLibrary"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json
new file mode 100644
index 0000000000000..073cd4a505d2f
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json
@@ -0,0 +1,9 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-apple-macosx",
+  "namespace": {
+    "kind": "CompilationUnit",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json
new file mode 100644
index 0000000000000..f9b0c3351c0ee
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json
@@ -0,0 +1,9 @@
+{
+  "type": "StaticLibrary",
+  "target_triple": "arm64-macosx-apple",
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "members": []
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json
new file mode 100644
index 0000000000000..3059d007a2e9e
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json
@@ -0,0 +1,9 @@
+{
+  "members": [],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libempty"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json
new file mode 100644
index 0000000000000..7e1636d2740c8
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json
@@ -0,0 +1,43 @@
+{
+  "members": [
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "a.cpp"
+      },
+      "type": "TUSummary"
+    },
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "b.cpp"
+      },
+      "type": "TUSummary"
+    },
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "c.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libmulti"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json
 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json
new file mode 100644
index 0000000000000..7ad6021fbc447
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json
@@ -0,0 +1,21 @@
+{
+  "members": [
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "a.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libfoo"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test
new file mode 100644
index 0000000000000..78700ab886971
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test
@@ -0,0 +1,28 @@
+// Round-trip tests: read a StaticLibrary JSON input, write it back out,
+// and diff the result against the original.
+//
+// StaticLibrary has only an encoding representation, so --encoding is a
+// no-op for --type static-library and both invocations must produce the
+// same output.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// ============================================================================
+// Round-Trip Tests
+// ============================================================================
+
+// RUN: clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o 
%t/rt-empty.json
+// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty.json
+// RUN: clang-ssaf-format --type static-library --encoding 
%S/Inputs/rt-empty.json -o %t/rt-empty-enc.json
+// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty-enc.json
+
+// RUN: clang-ssaf-format --type static-library 
%S/Inputs/rt-single-member.json -o %t/rt-single-member.json
+// RUN: diff %S/Inputs/rt-single-member.json %t/rt-single-member.json
+// RUN: clang-ssaf-format --type static-library --encoding 
%S/Inputs/rt-single-member.json -o %t/rt-single-member-enc.json
+// RUN: diff %S/Inputs/rt-single-member.json %t/rt-single-member-enc.json
+
+// RUN: clang-ssaf-format --type static-library 
%S/Inputs/rt-multiple-members.json -o %t/rt-multiple-members.json
+// RUN: diff %S/Inputs/rt-multiple-members.json %t/rt-multiple-members.json
+// RUN: clang-ssaf-format --type static-library --encoding 
%S/Inputs/rt-multiple-members.json -o %t/rt-multiple-members-enc.json
+// RUN: diff %S/Inputs/rt-multiple-members.json %t/rt-multiple-members-enc.json
diff --git 
a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test 
b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test
new file mode 100644
index 0000000000000..3a7169cd33563
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test
@@ -0,0 +1,109 @@
+// Top-level StaticLibrary structure tests: target_triple, namespace
+// (StaticLibrary kind), members array, and per-member validation. Mirrors
+// the LUSummary top-level test pattern but covers the StaticLibrary-specific
+// shape.
+
+// ============================================================================
+// readStaticLibrary() / writeStaticLibrary() type-field errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/missing-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-TYPE
+// MISSING-TYPE:      clang-ssaf-format: error: reading StaticLibrary from 
file '{{.*}}missing-type.json'
+// MISSING-TYPE-NEXT: failed to read summary type from field 'type': expected 
JSON string
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/mismatched-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISMATCHED-TYPE
+// MISMATCHED-TYPE:      clang-ssaf-format: error: reading StaticLibrary from 
file '{{.*}}mismatched-type.json'
+// MISMATCHED-TYPE-NEXT: expected 'StaticLibrary' for field 'type' but got 
'TUSummary'
+
+// ============================================================================
+// target_triple errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/missing-target-triple.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=MISSING-TARGET-TRIPLE
+// MISSING-TARGET-TRIPLE:      clang-ssaf-format: error: reading StaticLibrary 
from file '{{.*}}missing-target-triple.json'
+// MISSING-TARGET-TRIPLE-NEXT: failed to read TargetTriple from field 
'target_triple': expected JSON string
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/not-normalized-target-triple.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=NOT-NORMALIZED-TARGET-TRIPLE
+// NOT-NORMALIZED-TARGET-TRIPLE:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}not-normalized-target-triple.json'
+// NOT-NORMALIZED-TARGET-TRIPLE-NEXT: reading TargetTriple from field 
'target_triple'
+// NOT-NORMALIZED-TARGET-TRIPLE-NEXT: target triple 'arm64-macosx-apple' is 
not in normalized form (expected 'arm64-apple-macosx')
+
+// ============================================================================
+// namespace errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/missing-namespace.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-NAMESPACE
+// MISSING-NAMESPACE:      clang-ssaf-format: error: reading StaticLibrary 
from file '{{.*}}missing-namespace.json'
+// MISSING-NAMESPACE-NEXT: failed to read BuildNamespace from field 
'namespace': expected JSON object
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/namespace-missing-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=NAMESPACE-MISSING-KIND
+// NAMESPACE-MISSING-KIND:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}namespace-missing-kind.json'
+// NAMESPACE-MISSING-KIND-NEXT: reading BuildNamespace from field 'namespace'
+// NAMESPACE-MISSING-KIND-NEXT: failed to read BuildNamespaceKind from field 
'kind': expected JSON string
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/namespace-invalid-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=NAMESPACE-INVALID-KIND
+// NAMESPACE-INVALID-KIND:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}namespace-invalid-kind.json'
+// NAMESPACE-INVALID-KIND-NEXT: reading BuildNamespace from field 'namespace'
+// NAMESPACE-INVALID-KIND-NEXT: reading BuildNamespaceKind from field 'kind'
+// NAMESPACE-INVALID-KIND-NEXT: invalid BuildNamespaceKind value 
'invalid_kind' for field 'kind'
+
+// StaticLibrary's namespace field must specifically have kind=StaticLibrary.
+// Any other well-formed kind (CompilationUnit, LinkUnit) is reported as a
+// mismatch.
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/namespace-wrong-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-WRONG-KIND
+// NAMESPACE-WRONG-KIND:      clang-ssaf-format: error: reading StaticLibrary 
from file '{{.*}}namespace-wrong-kind.json'
+// NAMESPACE-WRONG-KIND-NEXT: expected 'StaticLibrary' for field 
'namespace.kind' but got 'CompilationUnit'
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/namespace-missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=NAMESPACE-MISSING-NAME
+// NAMESPACE-MISSING-NAME:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}namespace-missing-name.json'
+// NAMESPACE-MISSING-NAME-NEXT: reading BuildNamespace from field 'namespace'
+// NAMESPACE-MISSING-NAME-NEXT: failed to read BuildNamespaceName from field 
'name': expected JSON string
+
+// ============================================================================
+// members array errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/missing-members.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-MEMBERS
+// MISSING-MEMBERS:      clang-ssaf-format: error: reading StaticLibrary from 
file '{{.*}}missing-members.json'
+// MISSING-MEMBERS-NEXT: failed to read StaticLibrary members from field 
'members': expected JSON array
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/members-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MEMBERS-NOT-ARRAY
+// MEMBERS-NOT-ARRAY:      clang-ssaf-format: error: reading StaticLibrary 
from file '{{.*}}members-not-array.json'
+// MEMBERS-NOT-ARRAY-NEXT: failed to read StaticLibrary members from field 
'members': expected JSON array
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/member-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MEMBER-NOT-OBJECT
+// MEMBER-NOT-OBJECT:      clang-ssaf-format: error: reading StaticLibrary 
from file '{{.*}}member-not-object.json'
+// MEMBER-NOT-OBJECT-NEXT: failed to read StaticLibrary member from index '0': 
expected JSON object
+
+// ============================================================================
+// per-member errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/member-mismatched-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=MEMBER-MISMATCHED-TYPE
+// MEMBER-MISMATCHED-TYPE:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}member-mismatched-type.json'
+// MEMBER-MISMATCHED-TYPE-NEXT: reading StaticLibrary member from index '0'
+// MEMBER-MISMATCHED-TYPE-NEXT: expected 'TUSummary' for field 'type' but got 
'LUSummary'
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/member-triple-mismatch.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines 
--check-prefix=MEMBER-TRIPLE-MISMATCH
+// MEMBER-TRIPLE-MISMATCH:      clang-ssaf-format: error: reading 
StaticLibrary from file '{{.*}}member-triple-mismatch.json'
+// MEMBER-TRIPLE-MISMATCH-NEXT: reading StaticLibrary member from index '0'
+// MEMBER-TRIPLE-MISMATCH-NEXT: expected 'arm64-apple-macosx' for field 
'target_triple' but got 'x86_64-apple-macosx'
+
+// RUN: not clang-ssaf-format --type static-library 
%S/Inputs/duplicate-member.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-MEMBER
+// DUPLICATE-MEMBER:      clang-ssaf-format: error: reading StaticLibrary from 
file '{{.*}}duplicate-member.json'
+// DUPLICATE-MEMBER-NEXT: failed to insert StaticLibrary member at index '1': 
encountered duplicate 'BuildNamespace(CompilationUnit, a.cpp)'
diff --git a/clang/tools/clang-ssaf-format/SSAFFormat.cpp 
b/clang/tools/clang-ssaf-format/SSAFFormat.cpp
index 9416b45c974b5..5b94da384cb7d 100644
--- a/clang/tools/clang-ssaf-format/SSAFFormat.cpp
+++ b/clang/tools/clang-ssaf-format/SSAFFormat.cpp
@@ -12,6 +12,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
 #include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h"
 #include 
"clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormatRegistry.h"
@@ -39,7 +40,7 @@ namespace {
 // Summary Type
 
//===----------------------------------------------------------------------===//
 
-enum class SummaryType { Auto, TU, LU, WPA };
+enum class SummaryType { Auto, TU, LU, StaticLibrary, WPA };
 
 
//===----------------------------------------------------------------------===//
 // Command-Line Options
@@ -63,6 +64,8 @@ cl::opt<SummaryType> Type(
                           "Detect type from the file's 'type' field"),
                clEnumValN(SummaryType::TU, "tu", "Translation unit summary"),
                clEnumValN(SummaryType::LU, "lu", "Link unit summary"),
+               clEnumValN(SummaryType::StaticLibrary, "static-library",
+                          "Static library of translation unit summaries"),
                clEnumValN(SummaryType::WPA, "wpa",
                           "Whole-program analysis suite")),
     cl::init(SummaryType::Auto), cl::cat(SsafFormatCategory));
@@ -295,6 +298,12 @@ void convert(const FormatInput &FI) {
           &SerializationFormat::writeLUSummary);
     }
     return;
+  case SummaryType::StaticLibrary:
+    // StaticLibrary has only an encoded representation, so --encoding is a
+    // no-op here: both paths route to readStaticLibrary / writeStaticLibrary.
+    run(FI, &SerializationFormat::readStaticLibrary,
+        &SerializationFormat::writeStaticLibrary);
+    return;
   case SummaryType::WPA:
     run(FI, &SerializationFormat::readWPASuite,
         &SerializationFormat::writeWPASuite);
diff --git a/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp 
b/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp
index e98efd45ea6b0..5089796a2b0b8 100644
--- a/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp
@@ -83,6 +83,8 @@ TEST(BuildNamespaceKindTest, FormatProvider) {
             "CompilationUnit");
   EXPECT_EQ(llvm::formatv("{0}", BuildNamespaceKind::LinkUnit).str(),
             "LinkUnit");
+  EXPECT_EQ(llvm::formatv("{0}", BuildNamespaceKind::StaticLibrary).str(),
+            "StaticLibrary");
 }
 
 TEST(BuildNamespaceKindTest, StreamOutputCompilationUnit) {
@@ -97,6 +99,12 @@ TEST(BuildNamespaceKindTest, StreamOutputLinkUnit) {
   EXPECT_EQ(S, "LinkUnit");
 }
 
+TEST(BuildNamespaceKindTest, StreamOutputStaticLibrary) {
+  std::string S;
+  llvm::raw_string_ostream(S) << BuildNamespaceKind::StaticLibrary;
+  EXPECT_EQ(S, "StaticLibrary");
+}
+
 TEST(BuildNamespaceTest, FormatProvider) {
   EXPECT_EQ(
       llvm::formatv("{0}", BuildNamespace(BuildNamespaceKind::CompilationUnit,
diff --git a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt 
b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
index 387f03dc76fc6..f87188b6e75d1 100644
--- a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
@@ -26,6 +26,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SourceTransformation/EmitterTest.cpp
   SourceTransformation/RegistryTest.cpp
+  StaticLibraryTest.cpp
   SummaryData/SummaryDataTest.cpp
   SummaryNameTest.cpp
   TestFixture.cpp
diff --git 
a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp
 
b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp
index 2d65fb675eec3..4baf593f57eef 100644
--- 
a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp
@@ -98,6 +98,16 @@ class FailingSerializationFormat final : public 
SerializationFormat {
     return failing("writeLUSummaryEncoding");
   }
 
+  llvm::Expected<StaticLibrary>
+  readStaticLibrary(llvm::StringRef Path) override {
+    return failing("readStaticLibrary");
+  }
+
+  llvm::Error writeStaticLibrary(const StaticLibrary &S,
+                                 llvm::StringRef Path) override {
+    return failing("writeStaticLibrary");
+  }
+
   llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override {
     return failing("readWPASuite");
   }
@@ -179,6 +189,13 @@ class CapturingSerializationFormat final : public 
SerializationFormat {
                                      llvm::StringRef) override {
     return llvm::Error::success();
   }
+  llvm::Expected<StaticLibrary> readStaticLibrary(llvm::StringRef) override {
+    return llvm::createStringError("not implemented");
+  }
+  llvm::Error writeStaticLibrary(const StaticLibrary &,
+                                 llvm::StringRef) override {
+    return llvm::Error::success();
+  }
   llvm::Expected<WPASuite> readWPASuite(llvm::StringRef) override {
     return llvm::createStringError("not implemented");
   }
diff --git 
a/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp 
b/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp
index ff24a8358612d..b0c741d83feaf 100644
--- a/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp
@@ -27,6 +27,11 @@ TEST(BuildNamespaceKindStringTest, ToStringLinkUnit) {
             "LinkUnit");
 }
 
+TEST(BuildNamespaceKindStringTest, ToStringStaticLibrary) {
+  EXPECT_EQ(buildNamespaceKindToString(BuildNamespaceKind::StaticLibrary),
+            "StaticLibrary");
+}
+
 TEST(BuildNamespaceKindStringTest, FromStringCompilationUnit) {
   EXPECT_EQ(buildNamespaceKindFromString("CompilationUnit"),
             BuildNamespaceKind::CompilationUnit);
@@ -37,9 +42,15 @@ TEST(BuildNamespaceKindStringTest, FromStringLinkUnit) {
             BuildNamespaceKind::LinkUnit);
 }
 
+TEST(BuildNamespaceKindStringTest, FromStringStaticLibrary) {
+  EXPECT_EQ(buildNamespaceKindFromString("StaticLibrary"),
+            BuildNamespaceKind::StaticLibrary);
+}
+
 TEST(BuildNamespaceKindStringTest, FromStringUnknown) {
   EXPECT_EQ(buildNamespaceKindFromString("compilation_unit"), std::nullopt);
   EXPECT_EQ(buildNamespaceKindFromString("link_unit"), std::nullopt);
+  EXPECT_EQ(buildNamespaceKindFromString("static_library"), std::nullopt);
   EXPECT_EQ(buildNamespaceKindFromString(""), std::nullopt);
   EXPECT_EQ(buildNamespaceKindFromString("unknown"), std::nullopt);
 }
@@ -51,6 +62,9 @@ TEST(BuildNamespaceKindStringTest, RoundTrip) {
   EXPECT_EQ(buildNamespaceKindFromString(
                 buildNamespaceKindToString(BuildNamespaceKind::LinkUnit)),
             BuildNamespaceKind::LinkUnit);
+  EXPECT_EQ(buildNamespaceKindFromString(
+                buildNamespaceKindToString(BuildNamespaceKind::StaticLibrary)),
+            BuildNamespaceKind::StaticLibrary);
 }
 
 
//===----------------------------------------------------------------------===//
diff --git 
a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp 
b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp
index 915fe15af832c..4a01248b53ac7 100644
--- 
a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp
@@ -9,6 +9,7 @@
 #include "Registries/MockSerializationFormat.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h"
@@ -198,6 +199,16 @@ llvm::Error 
MockSerializationFormat::writeLUSummaryEncoding(
       "MockSerializationFormat does not support LUSummaryEncoding");
 }
 
+llvm::Expected<StaticLibrary>
+MockSerializationFormat::readStaticLibrary(llvm::StringRef Path) {
+  llvm_unreachable("MockSerializationFormat does not support StaticLibrary");
+}
+
+llvm::Error MockSerializationFormat::writeStaticLibrary(const StaticLibrary &S,
+                                                        llvm::StringRef Path) {
+  llvm_unreachable("MockSerializationFormat does not support StaticLibrary");
+}
+
 llvm::Expected<WPASuite>
 MockSerializationFormat::readWPASuite(llvm::StringRef Path) {
   llvm_unreachable("MockSerializationFormat does not support WPASuite");
diff --git 
a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h 
b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h
index 2ecaab9fb0132..0cadd0b98fe3a 100644
--- 
a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h
+++ 
b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h
@@ -44,6 +44,12 @@ class MockSerializationFormat final : public 
SerializationFormat {
   llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
                                      llvm::StringRef Path) override;
 
+  llvm::Expected<StaticLibrary>
+  readStaticLibrary(llvm::StringRef Path) override;
+
+  llvm::Error writeStaticLibrary(const StaticLibrary &S,
+                                 llvm::StringRef Path) override;
+
   llvm::Expected<Artifact> readArtifact(llvm::StringRef Path) override;
 
   llvm::Error writeArtifact(const Artifact &A, llvm::StringRef Path) override;
diff --git a/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp 
b/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp
new file mode 100644
index 0000000000000..b4f4a987d3fb5
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp
@@ -0,0 +1,33 @@
+//===- StaticLibraryTest.cpp 
----------------------------------------------===//
+//
+// 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/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
+#include "gtest/gtest.h"
+
+namespace clang::ssaf {
+namespace {
+
+TEST(StaticLibraryTest, GetNamespace) {
+  BuildNamespace BN(BuildNamespaceKind::StaticLibrary, "libfoo");
+  StaticLibrary S(llvm::Triple("arm64-apple-macosx"), BN);
+
+  EXPECT_EQ(S.getNamespace(), BN);
+}
+
+TEST(StaticLibraryTest, GetTargetTriple) {
+  llvm::Triple T("arm64-apple-macosx");
+  StaticLibrary S(T,
+                  BuildNamespace(BuildNamespaceKind::StaticLibrary, "libfoo"));
+
+  EXPECT_EQ(S.getTargetTriple(), T);
+}
+
+} // namespace
+} // namespace clang::ssaf
diff --git a/clang/unittests/ScalableStaticAnalysis/TestFixture.h 
b/clang/unittests/ScalableStaticAnalysis/TestFixture.h
index 22fbff754fb37..e7eed940938ce 100644
--- a/clang/unittests/ScalableStaticAnalysis/TestFixture.h
+++ b/clang/unittests/ScalableStaticAnalysis/TestFixture.h
@@ -11,6 +11,7 @@
 
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"

>From 76cdd13143913d828d6f8ec418fda944705b80b7 Mon Sep 17 00:00:00 2001
From: Aviral Goel <[email protected]>
Date: Thu, 25 Jun 2026 16:39:52 -0700
Subject: [PATCH 2/4] Remove unnecessary test

---
 .../Core/EntityLinker/StaticLibrary.h         |  4 ---
 .../ScalableStaticAnalysis/CMakeLists.txt     |  1 -
 .../StaticLibraryTest.cpp                     | 33 -------------------
 3 files changed, 38 deletions(-)
 delete mode 100644 clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp

diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
index 9027a603722c5..b0413030de394 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
@@ -62,10 +62,6 @@ class StaticLibrary {
   StaticLibrary(llvm::Triple TargetTriple, BuildNamespace Namespace)
       : TargetTriple(std::move(TargetTriple)), Namespace(std::move(Namespace)) 
{
   }
-
-  const llvm::Triple &getTargetTriple() const { return TargetTriple; }
-
-  const BuildNamespace &getNamespace() const { return Namespace; }
 };
 
 } // namespace clang::ssaf
diff --git a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt 
b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
index f87188b6e75d1..387f03dc76fc6 100644
--- a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
@@ -26,7 +26,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SourceTransformation/EmitterTest.cpp
   SourceTransformation/RegistryTest.cpp
-  StaticLibraryTest.cpp
   SummaryData/SummaryDataTest.cpp
   SummaryNameTest.cpp
   TestFixture.cpp
diff --git a/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp 
b/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp
deleted file mode 100644
index b4f4a987d3fb5..0000000000000
--- a/clang/unittests/ScalableStaticAnalysis/StaticLibraryTest.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-//===- StaticLibraryTest.cpp 
----------------------------------------------===//
-//
-// 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/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
-#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
-#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
-#include "gtest/gtest.h"
-
-namespace clang::ssaf {
-namespace {
-
-TEST(StaticLibraryTest, GetNamespace) {
-  BuildNamespace BN(BuildNamespaceKind::StaticLibrary, "libfoo");
-  StaticLibrary S(llvm::Triple("arm64-apple-macosx"), BN);
-
-  EXPECT_EQ(S.getNamespace(), BN);
-}
-
-TEST(StaticLibraryTest, GetTargetTriple) {
-  llvm::Triple T("arm64-apple-macosx");
-  StaticLibrary S(T,
-                  BuildNamespace(BuildNamespaceKind::StaticLibrary, "libfoo"));
-
-  EXPECT_EQ(S.getTargetTriple(), T);
-}
-
-} // namespace
-} // namespace clang::ssaf

>From 84d99f3a3266417bdba26f889c026df6081558ec Mon Sep 17 00:00:00 2001
From: Aviral Goel <[email protected]>
Date: Thu, 25 Jun 2026 16:49:18 -0700
Subject: [PATCH 3/4] Move

---
 .../Core/EntityLinker/StaticLibrary.h         | 39 ++++++++++++++++---
 .../Core/EntityLinker/TUSummaryEncoding.h     | 28 +------------
 2 files changed, 35 insertions(+), 32 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
index b0413030de394..8d3c789a13756 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
@@ -43,6 +43,38 @@ class StaticLibrary {
   friend class SerializationFormat;
   friend class TestFixture;
 
+  /// Reads a member's TUNamespace via the friend grant on StaticLibrary
+  /// declared in TUSummaryEncoding.h. Exists solely so MemberByNamespace
+  /// can compare members by namespace without exposing a public accessor
+  /// on TUSummaryEncoding. As a nested member, MemberByNamespace can call
+  /// this even though it is not itself a friend of TUSummaryEncoding.
+  static const BuildNamespace &memberNamespace(const TUSummaryEncoding &SE) {
+    return SE.TUNamespace;
+  }
+
+  /// Comparator that orders members by their TUNamespace. Private detail of
+  /// StaticLibrary; not part of TUSummaryEncoding's public API. The
+  /// transparent overloads enable std::set::find() lookup by a bare
+  /// BuildNamespace.
+  struct MemberByNamespace {
+    using is_transparent = void;
+
+    bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
+                    const std::unique_ptr<TUSummaryEncoding> &B) const {
+      return memberNamespace(*A) < memberNamespace(*B);
+    }
+
+    bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
+                    const BuildNamespace &B) const {
+      return memberNamespace(*A) < B;
+    }
+
+    bool operator()(const BuildNamespace &A,
+                    const std::unique_ptr<TUSummaryEncoding> &B) const {
+      return A < memberNamespace(*B);
+    }
+  };
+
   // Target triple of the static library. All member TUs must share this
   // triple.
   llvm::Triple TargetTriple;
@@ -52,11 +84,8 @@ class StaticLibrary {
 
   // Member translation units, ordered by their TUNamespace. Membership is
   // by namespace identity: inserting a TU whose TUNamespace already exists
-  // in the set is rejected during deserialization. The comparator reaches
-  // the members' private TUNamespace field via friend access on
-  // TUSummaryEncoding rather than through a public accessor.
-  std::set<std::unique_ptr<TUSummaryEncoding>, TUSummaryEncodingByNamespace>
-      Members;
+  // in the set is rejected during deserialization.
+  std::set<std::unique_ptr<TUSummaryEncoding>, MemberByNamespace> Members;
 
 public:
   StaticLibrary(llvm::Triple TargetTriple, BuildNamespace Namespace)
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
index 6936bbc8fcffc..2ece195ed1fd0 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
@@ -26,8 +26,6 @@
 
 namespace clang::ssaf {
 
-struct TUSummaryEncodingByNamespace;
-
 /// Represents a translation unit summary in its serialized encoding.
 ///
 /// TUSummaryEncoding holds entity summary data in a format-specific encoding
@@ -37,8 +35,8 @@ struct TUSummaryEncodingByNamespace;
 class TUSummaryEncoding {
   friend class EntityLinker;
   friend class SerializationFormat;
+  friend class StaticLibrary;
   friend class TestFixture;
-  friend struct TUSummaryEncodingByNamespace;
 
   // Target triple of the translation unit.
   llvm::Triple TargetTriple;
@@ -65,30 +63,6 @@ class TUSummaryEncoding {
   const llvm::Triple &getTargetTriple() const { return TargetTriple; }
 };
 
-/// Comparator that orders TUSummaryEncoding pointers by their TUNamespace.
-/// Lives here (next to TUSummaryEncoding) rather than inside StaticLibrary
-/// so it can reach the private TUNamespace field via friend access without
-/// exposing a public accessor. The transparent overloads enable
-/// std::set::find() lookup by a bare BuildNamespace.
-struct TUSummaryEncodingByNamespace {
-  using is_transparent = void;
-
-  bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
-                  const std::unique_ptr<TUSummaryEncoding> &B) const {
-    return A->TUNamespace < B->TUNamespace;
-  }
-
-  bool operator()(const std::unique_ptr<TUSummaryEncoding> &A,
-                  const BuildNamespace &B) const {
-    return A->TUNamespace < B;
-  }
-
-  bool operator()(const BuildNamespace &A,
-                  const std::unique_ptr<TUSummaryEncoding> &B) const {
-    return A < B->TUNamespace;
-  }
-};
-
 } // namespace clang::ssaf
 
 #endif // 
LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_TUSUMMARYENCODING_H

>From 40f4639e35584cc5ceac66c1a8b4d6786facc302 Mon Sep 17 00:00:00 2001
From: Aviral Goel <[email protected]>
Date: Thu, 25 Jun 2026 16:50:43 -0700
Subject: [PATCH 4/4] sort

---
 .../ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def 
b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
index 6354ebd06ae41..b1c37823dc3fe 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def
@@ -19,9 +19,6 @@
 
 FIELD(BuildNamespace, Kind)
 FIELD(BuildNamespace, Name)
-FIELD(StaticLibrary, TargetTriple)
-FIELD(StaticLibrary, Namespace)
-FIELD(StaticLibrary, Members)
 FIELD(EntityId, Index)
 FIELD(EntityIdTable, Entities)
 FIELD(EntityLinkage, Linkage)
@@ -39,6 +36,9 @@ FIELD(LUSummaryEncoding, IdTable)
 FIELD(LUSummaryEncoding, LinkageTable)
 FIELD(LUSummaryEncoding, LUNamespace)
 FIELD(NestedBuildNamespace, Namespaces)
+FIELD(StaticLibrary, Members)
+FIELD(StaticLibrary, Namespace)
+FIELD(StaticLibrary, TargetTriple)
 FIELD(TUSummary, TargetTriple)
 FIELD(TUSummary, Data)
 FIELD(TUSummary, IdTable)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to