[clang] [clang][ssaf] Add Entity Linker and associated data structures (PR #181765)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 16 17:32:09 PST 2026
https://github.com/aviralg created https://github.com/llvm/llvm-project/pull/181765
None
>From a513fdb439c2245b22440d0658db936f79e1ca81 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Feb 2026 17:29:25 -0800
Subject: [PATCH] Add Entity Linker algorithm and associated data structures
---
.../Scalable/EntityLinker/EntityLinker.h | 65 ++++++
.../EntityLinker/EntitySummaryEncoding.h | 41 ++++
.../Scalable/EntityLinker/LUSummary.h | 56 +++++
.../Scalable/EntityLinker/LUSummaryEncoding.h | 57 ++++++
.../Scalable/EntityLinker/TUSummaryEncoding.h | 59 ++++++
.../clang/Analysis/Scalable/Model/EntityId.h | 7 +-
.../Analysis/Scalable/Model/EntityIdTable.h | 5 +-
.../Analysis/Scalable/Model/EntityLinkage.h | 4 +-
.../Analysis/Scalable/Model/EntityName.h | 1 +
.../Analysis/Scalable/Support/ErrorBuilder.h | 89 ++++++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 2 +
.../Scalable/EntityLinker/EntityLinker.cpp | 192 ++++++++++++++++++
.../Scalable/Serialization/JSONFormat.cpp | 97 +--------
.../Scalable/Support/ErrorBuilder.cpp | 59 ++++++
14 files changed, 631 insertions(+), 103 deletions(-)
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
create mode 100644 clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
create mode 100644 clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
new file mode 100644
index 0000000000000..b628af9d25843
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
@@ -0,0 +1,65 @@
+//===- EntityLinker.h - Class for linking entities --------------*- 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 EntityLinker class that combines multiple TU summaries
+// into a unified LU summary by deduplicating entities and patching summaries.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace clang::ssaf {
+
+class EntitySummaryEncoding;
+class TUSummaryEncoding;
+
+class EntityLinker {
+ LUSummaryEncoding Output;
+
+public:
+ EntityLinker(NestedBuildNamespace LUNamespace)
+ : Output(std::move(LUNamespace)) {}
+
+ llvm::Error link(std::unique_ptr<TUSummaryEncoding> Summary);
+
+ const LUSummaryEncoding &getOutput() const { return Output; }
+
+private:
+ llvm::Expected<EntityId> resolve(const EntityName &OldName,
+ const EntityId OldId,
+ const EntityLinkage &EL);
+
+ llvm::Error
+ merge(std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &InputData,
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &OutputData,
+ const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ std::vector<EntitySummaryEncoding *> &PatchTargets);
+
+ void patch(std::vector<EntitySummaryEncoding *> &PatchTargets,
+ const std::map<EntityId, EntityId> &EntityResolutionTable);
+};
+
+} // end namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
new file mode 100644
index 0000000000000..a38dd0c895452
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
@@ -0,0 +1,41 @@
+//===- EntitySummaryEncoding.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 EntitySummaryEncoding class, which represents
+// EntitySummary data in an encoded, format-specific form.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include <map>
+
+namespace clang::ssaf {
+
+/// Represents EntitySummary data in its serialized, format-specific encoding.
+///
+/// This abstract base class allows the entity linker to manipulate serialized
+/// entity summary data without knowing the exact schema of the EntitySummary
+/// subclass. The primary operation is patching EntityId references when
+/// entities are merged during linking.
+class EntitySummaryEncoding {
+public:
+ virtual ~EntitySummaryEncoding() = default;
+
+ /// Updates EntityId references in the encoded data.
+ ///
+ /// \param EntityResolutionTable Mapping from old EntityIds to new EntityIds.
+ virtual void
+ patch(const std::map<EntityId, EntityId> &EntityResolutionTable) = 0;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
new file mode 100644
index 0000000000000..54d18d78d53bf
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -0,0 +1,56 @@
+//===- LUSummary.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 LUSummary class, which represents a link unit summary
+// containing merged and deduplicated entity summaries from multiple TUs.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_H
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntitySummary;
+class SerializationFormat;
+class SummaryViewBuilder;
+
+/// Represents a link unit (LU) summary containing merged entity summaries.
+///
+/// LUSummary is the result of linking multiple translation unit summaries
+/// together. It contains deduplicated entities with their linkage information
+/// and the merged entity summaries.
+class LUSummary {
+ NestedBuildNamespace LUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummary>>>
+ Data;
+
+public:
+ LUSummary(NestedBuildNamespace LUNamespace)
+ : LUNamespace(std::move(LUNamespace)) {}
+
+ friend class SerializationFormat;
+ friend class SummaryViewBuilder;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
new file mode 100644
index 0000000000000..39185990a9ea6
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
@@ -0,0 +1,57 @@
+//===- LUSummaryEncoding.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 LUSummaryEncoding class, which represents a link unit
+// summary in its serialized, format-specific encoding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntityLinker;
+class SerializationFormat;
+
+/// Represents a link unit summary in its serialized encoding.
+///
+/// LUSummaryEncoding holds the combined entity summary data from multiple
+/// translation units in a format-specific encoding. It is produced by the
+/// entity linker and contains deduplicated and patched entity summaries.
+class LUSummaryEncoding {
+ NestedBuildNamespace LUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ Data;
+
+public:
+ LUSummaryEncoding(NestedBuildNamespace LUNamespace)
+ : LUNamespace(std::move(LUNamespace)) {}
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
new file mode 100644
index 0000000000000..1b42eb70d09d0
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
@@ -0,0 +1,59 @@
+//===- TUSummaryEncoding.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 TUSummaryEncoding class, which represents a
+// translation unit summary in its serialized, format-specific encoding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntityLinker;
+class SerializationFormat;
+
+/// Represents a translation unit summary in its serialized encoding.
+///
+/// TUSummaryEncoding holds entity summary data in a format-specific encoding
+/// that can be manipulated by the entity linker without deserializing the
+/// full EntitySummary objects. This enables efficient entity ID patching
+/// during the linking process.
+class TUSummaryEncoding {
+ /// Identifies the translation unit.
+ BuildNamespace TUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ Data;
+
+public:
+ TUSummaryEncoding(BuildNamespace TUNamespace)
+ : TUNamespace(std::move(TUNamespace)) {}
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index e348486386cb6..231525b445ca0 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -28,9 +28,6 @@ class EntityIdTable;
///
/// \see EntityIdTable
class EntityId {
- friend class EntityIdTable;
- friend class SerializationFormat;
-
size_t Index;
explicit EntityId(size_t Index) : Index(Index) {}
@@ -41,6 +38,10 @@ class EntityId {
bool operator==(const EntityId &Other) const { return Index == Other.Index; }
bool operator<(const EntityId &Other) const { return Index < Other.Index; }
bool operator!=(const EntityId &Other) const { return !(*this == Other); }
+
+ friend class EntityIdTable;
+ friend class EntityLinker;
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
index a1099c4e4d0f8..b12d3e0c0faec 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
@@ -21,8 +21,6 @@ namespace clang::ssaf {
/// The table maps each unique EntityName to exactly one EntityId.
/// Entities are never removed.
class EntityIdTable {
- friend class SerializationFormat;
-
std::map<EntityName, EntityId> Entities;
public:
@@ -45,6 +43,9 @@ class EntityIdTable {
/// Returns the number of unique entities in the table.
size_t count() const { return Entities.size(); }
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
index ba5f7d3073a30..af775769dc1e4 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -17,8 +17,6 @@ namespace clang::ssaf {
/// or external linkage, which determines its visibility and accessibility
/// across translation units.
class EntityLinkage {
- friend class SerializationFormat;
-
public:
enum class LinkageType {
None, ///< local variables, function parameters
@@ -32,6 +30,8 @@ class EntityLinkage {
private:
LinkageType Linkage;
+
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 23890ab7bea43..6bf51844f2f5b 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -47,6 +47,7 @@ class EntityName {
/// \param Namespace The namespace steps to append to this entity's namespace.
EntityName makeQualified(NestedBuildNamespace Namespace) const;
+ friend class EntityLinker;
friend class LinkUnitResolution;
friend class SerializationFormat;
};
diff --git a/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
new file mode 100644
index 0000000000000..4ccda48e2391a
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
@@ -0,0 +1,89 @@
+//===- ErrorBuilder.h - Fluent API for contextual errors --------*- 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 ErrorBuilder class, which provides a fluent API for
+// constructing contextual error messages with layered context information.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace clang::ssaf {
+
+/// Fluent API for constructing contextual errors.
+///
+/// ErrorBuilder allows building error messages with layered context
+/// information. Context is added from innermost to outermost, and the final
+/// error message presents the context in reverse order (outermost first).
+///
+/// Example usage:
+/// return ErrorBuilder::create(std::errc::invalid_argument,
+/// "invalid value {0}", value)
+/// .context("processing field '{0}'", fieldName)
+/// .context("reading configuration")
+/// .build();
+class ErrorBuilder {
+private:
+ std::error_code Code;
+ std::vector<std::string> ContextStack;
+
+ // Private constructor - only accessible via static factories.
+ explicit ErrorBuilder(std::error_code EC) : Code(EC) {}
+
+ // Helper: Format message and add to context stack.
+ template <typename... Args>
+ void addFormattedContext(const char *Fmt, Args &&...ArgVals) {
+ std::string Message =
+ llvm::formatv(Fmt, std::forward<Args>(ArgVals)...).str();
+ ContextStack.push_back(std::move(Message));
+ }
+
+public:
+ // Static factory: Create new error from error code and formatted message.
+ template <typename... Args>
+ static ErrorBuilder create(std::error_code EC, const char *Fmt,
+ Args &&...ArgVals) {
+ ErrorBuilder Builder(EC);
+ Builder.addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
+ return Builder;
+ }
+
+ // Convenience overload for std::errc.
+ template <typename... Args>
+ static ErrorBuilder create(std::errc EC, const char *Fmt, Args &&...ArgVals) {
+ return create(std::make_error_code(EC), Fmt,
+ std::forward<Args>(ArgVals)...);
+ }
+
+ // Static factory: Wrap existing error and optionally add context.
+ static ErrorBuilder wrap(llvm::Error E);
+
+ // Add context (plain string).
+ ErrorBuilder &context(const char *Msg);
+
+ // Add context (formatted string).
+ template <typename... Args>
+ ErrorBuilder &context(const char *Fmt, Args &&...ArgVals) {
+ addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
+ return *this;
+ }
+
+ // Build the final error.
+ llvm::Error build();
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 522fc9dcf078d..8ef1625cc704b 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -4,11 +4,13 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangAnalysisScalable
ASTEntityMapping.cpp
+ EntityLinker/EntityLinker.cpp
Model/BuildNamespace.cpp
Model/EntityIdTable.cpp
Model/EntityName.cpp
Serialization/JSONFormat.cpp
Serialization/SerializationFormatRegistry.cpp
+ Support/ErrorBuilder.cpp
TUSummary/ExtractorRegistry.cpp
LINK_LIBS
diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
new file mode 100644
index 0000000000000..2c7344b19de55
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
@@ -0,0 +1,192 @@
+//===- EntityLinker.cpp ----------------------------------------*- 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/Analysis/Scalable/EntityLinker/EntityLinker.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace clang::ssaf;
+
+//----------------------------------------------------------------------------
+// Error Message Constants
+//----------------------------------------------------------------------------
+
+namespace {
+
+namespace ErrorMessages {
+
+constexpr const char *EntityIdAlreadyExistsInLinkageTable =
+ "EntityId({0}) already exists in LU linkage table";
+
+constexpr const char *FailedToMergeSummaryData =
+ "failed to merge summary data for TU EntityId({0}) resolved to LU "
+ "EntityId({1}) with linkage '{2}'";
+
+constexpr const char *MissingLinkageInformation =
+ "missing linkage information for TU EntityId({0})";
+
+constexpr const char *DuplicateEntityIdInLinking =
+ "duplicate TU EntityId({0}) encountered during linking";
+
+constexpr const char *ResolvingEntity =
+ "resolving entity '{0}' (TU EntityId({1}))";
+constexpr const char *MergingSummaryData = "merging summary data";
+constexpr const char *LinkingTUSummary = "linking TU summary";
+
+} // namespace ErrorMessages
+
+} // namespace
+
+static NestedBuildNamespace
+resolveNamespace(const NestedBuildNamespace &LUNamespace,
+ const NestedBuildNamespace &EntityNamespace,
+ const EntityLinkage::LinkageType Linkage) {
+ switch (Linkage) {
+ case EntityLinkage::LinkageType::None:
+ case EntityLinkage::LinkageType::Internal:
+ return EntityNamespace.makeQualified(LUNamespace);
+ case EntityLinkage::LinkageType::External:
+ return NestedBuildNamespace(LUNamespace);
+ }
+
+ llvm_unreachable("Unhandled EntityLinkage::LinkageType variant");
+}
+
+llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
+ const EntityId OldId,
+ const EntityLinkage &EL) {
+ NestedBuildNamespace NewNamespace =
+ resolveNamespace(Output.LUNamespace, OldName.Namespace, EL.getLinkage());
+
+ EntityName NewName(OldName.USR, OldName.Suffix, NewNamespace);
+
+ // NewId construction will always return a fresh id for `None` and `Internal`
+ // linkage entities since their namespaces will be different even if their
+ // names clash. For `External` linkage entities with clashing names this
+ // function will return the id assigned at the first insertion.
+ EntityId NewId = Output.IdTable.getId(NewName);
+
+ auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, EL);
+ if (!Inserted) {
+ return ErrorBuilder::create(
+ llvm::inconvertibleErrorCode(),
+ ErrorMessages::EntityIdAlreadyExistsInLinkageTable, NewId.Index)
+ .build();
+ }
+
+ return NewId;
+}
+
+llvm::Error EntityLinker::merge(
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &InputData,
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &OutputData,
+ const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ std::vector<EntitySummaryEncoding *> &PatchTargets) {
+ for (auto &[Name, DataMap] : InputData) {
+ auto Iter = DataMap.find(OldId);
+ if (Iter == DataMap.end())
+ continue;
+
+ auto &OutputMap = OutputData[Name];
+ auto Result = OutputMap.insert({NewId, std::move(Iter->second)});
+
+ // If insertion is successful, we will have to replace OldId with NewId in
+ // this EntitySummaryEncoding.
+ if (Result.second) {
+ PatchTargets.push_back(Result.first->second.get());
+ } else {
+ switch (EL.getLinkage()) {
+ // Insertion should never fail for `None` and `Internal` linkage
+ // entities because these entities have different namespaces even if
+ // their names clash.
+ case EntityLinkage::LinkageType::None:
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::FailedToMergeSummaryData,
+ OldId.Index, NewId.Index, "None")
+ .build();
+ case EntityLinkage::LinkageType::Internal:
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::FailedToMergeSummaryData,
+ OldId.Index, NewId.Index, "Internal")
+ .build();
+ case EntityLinkage::LinkageType::External:
+ // Insertion is expected to fail for duplicate occurrences of `External`
+ // linkage entities. We will report these cases to help users debug
+ // potential ODR violations.
+ // TODO - issue diagnostic log for dropping data using instrumentation
+ // framework.
+ break;
+ }
+ }
+ }
+
+ return llvm::Error::success();
+}
+
+void EntityLinker::patch(
+ std::vector<EntitySummaryEncoding *> &PatchTargets,
+ const std::map<EntityId, EntityId> &EntityResolutionTable) {
+ for (auto *PatchTarget : PatchTargets) {
+ PatchTarget->patch(EntityResolutionTable);
+ }
+}
+
+llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) {
+ std::map<EntityId, EntityId> EntityResolutionTable;
+ std::vector<EntitySummaryEncoding *> PatchTargets;
+
+ for (const auto &[OldName, OldId] : Summary->IdTable.Entities) {
+
+ auto Iter = Summary->LinkageTable.find(OldId);
+ if (Iter == Summary->LinkageTable.end()) {
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::MissingLinkageInformation,
+ OldId.Index)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ EntityLinkage &Linkage = Iter->second;
+
+ llvm::Expected<EntityId> NewIdOrErr = resolve(OldName, OldId, Linkage);
+ if (!NewIdOrErr)
+ return ErrorBuilder::wrap(NewIdOrErr.takeError())
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+
+ EntityId NewId = *NewIdOrErr;
+
+ auto Res = EntityResolutionTable.insert({OldId, NewId});
+ if (!Res.second) {
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::DuplicateEntityIdInLinking,
+ OldId.Index)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ if (llvm::Error Err = merge(Summary->Data, Output.Data, OldId, NewId,
+ Linkage, PatchTargets))
+ return ErrorBuilder::wrap(std::move(Err))
+ .context(ErrorMessages::MergingSummaryData)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ patch(PatchTargets, EntityResolutionTable);
+
+ return llvm::Error::success();
+}
diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
index 6f7de45e863d1..0007ddf6a275f 100644
--- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
+++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
@@ -1,4 +1,5 @@
#include "clang/Analysis/Scalable/Serialization/JSONFormat.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
#include "llvm/ADT/StringExtras.h"
@@ -15,102 +16,6 @@ using Array = llvm::json::Array;
using Object = llvm::json::Object;
using Value = llvm::json::Value;
-//----------------------------------------------------------------------------
-// ErrorBuilder - Fluent API for constructing contextual errors.
-//----------------------------------------------------------------------------
-
-namespace {
-
-class ErrorBuilder {
-private:
- std::error_code Code;
- std::vector<std::string> ContextStack;
-
- // Private constructor - only accessible via static factories.
- explicit ErrorBuilder(std::error_code EC) : Code(EC) {}
-
- // Helper: Format message and add to context stack.
- template <typename... Args>
- void addFormattedContext(const char *Fmt, Args &&...ArgVals) {
- std::string Message =
- llvm::formatv(Fmt, std::forward<Args>(ArgVals)...).str();
- ContextStack.push_back(std::move(Message));
- }
-
-public:
- // Static factory: Create new error from error code and formatted message.
- template <typename... Args>
- static ErrorBuilder create(std::error_code EC, const char *Fmt,
- Args &&...ArgVals) {
- ErrorBuilder Builder(EC);
- Builder.addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
- return Builder;
- }
-
- // Convenience overload for std::errc.
- template <typename... Args>
- static ErrorBuilder create(std::errc EC, const char *Fmt, Args &&...ArgVals) {
- return create(std::make_error_code(EC), Fmt,
- std::forward<Args>(ArgVals)...);
- }
-
- // Static factory: Wrap existing error and optionally add context.
- static ErrorBuilder wrap(llvm::Error E) {
- if (!E) {
- llvm::consumeError(std::move(E));
- // Return builder with generic error code for success case.
- return ErrorBuilder(std::make_error_code(std::errc::invalid_argument));
- }
-
- std::error_code EC;
- bool FirstError = true;
- ErrorBuilder Builder(std::make_error_code(std::errc::invalid_argument));
-
- llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
- // Capture error code from the first error only.
- if (FirstError) {
- EC = EI.convertToErrorCode();
- Builder.Code = EC;
- FirstError = false;
- }
-
- // Collect messages from all errors.
- std::string ErrorMsg = EI.message();
- if (!ErrorMsg.empty()) {
- Builder.ContextStack.push_back(std::move(ErrorMsg));
- }
- });
-
- return Builder;
- }
-
- // Add context (plain string).
- ErrorBuilder &context(const char *Msg) {
- ContextStack.push_back(Msg);
- return *this;
- }
-
- // Add context (formatted string).
- template <typename... Args>
- ErrorBuilder &context(const char *Fmt, Args &&...ArgVals) {
- addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
- return *this;
- }
-
- // Build the final error.
- llvm::Error build() {
- if (ContextStack.empty())
- return llvm::Error::success();
-
- // Reverse the context stack so that the most recent context appears first
- // and the wrapped error (if any) appears last.
- return llvm::createStringError(
- llvm::join(llvm::reverse(ContextStack), "\n"), Code);
- }
-};
-
-} // namespace
-
//----------------------------------------------------------------------------
// File Format Constant
//----------------------------------------------------------------------------
diff --git a/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
new file mode 100644
index 0000000000000..ad0a013bcf2f2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
@@ -0,0 +1,59 @@
+//===- ErrorBuilder.cpp ----------------------------------------*- 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/Analysis/Scalable/Support/ErrorBuilder.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace clang::ssaf {
+
+ErrorBuilder ErrorBuilder::wrap(llvm::Error E) {
+ if (!E) {
+ llvm::consumeError(std::move(E));
+ // Return builder with generic error code for success case.
+ return ErrorBuilder(std::make_error_code(std::errc::invalid_argument));
+ }
+
+ std::error_code EC;
+ bool FirstError = true;
+ ErrorBuilder Builder(std::make_error_code(std::errc::invalid_argument));
+
+ llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
+ // Capture error code from the first error only.
+ if (FirstError) {
+ EC = EI.convertToErrorCode();
+ Builder.Code = EC;
+ FirstError = false;
+ }
+
+ // Collect messages from all errors.
+ std::string ErrorMsg = EI.message();
+ if (!ErrorMsg.empty()) {
+ Builder.ContextStack.push_back(std::move(ErrorMsg));
+ }
+ });
+
+ return Builder;
+}
+
+ErrorBuilder &ErrorBuilder::context(const char *Msg) {
+ ContextStack.push_back(Msg);
+ return *this;
+}
+
+llvm::Error ErrorBuilder::build() {
+ if (ContextStack.empty())
+ return llvm::Error::success();
+
+ // Reverse the context stack so that the most recent context appears first
+ // and the wrapped error (if any) appears last.
+ return llvm::createStringError(llvm::join(llvm::reverse(ContextStack), "\n"),
+ Code);
+}
+
+} // namespace clang::ssaf
More information about the cfe-commits
mailing list