https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/181765
>From a513fdb439c2245b22440d0658db936f79e1ca81 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 16 Feb 2026 17:29:25 -0800 Subject: [PATCH 1/2] 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 >From 848d72a7612a881d518f0b49a391398aeb78cf68 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 16 Feb 2026 17:44:17 -0800 Subject: [PATCH 2/2] Quality Fixes --- .../Scalable/EntityLinker/EntityLinker.h | 5 ++-- .../Analysis/Scalable/Model/EntityLinkage.h | 5 ++++ clang/lib/Analysis/Scalable/CMakeLists.txt | 1 + .../Scalable/EntityLinker/EntityLinker.cpp | 29 +++++++------------ .../Analysis/Scalable/Model/EntityLinkage.cpp | 27 +++++++++++++++++ 5 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h index b628af9d25843..4788f27ce24a0 100644 --- a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h +++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h @@ -44,7 +44,7 @@ class EntityLinker { private: llvm::Expected<EntityId> resolve(const EntityName &OldName, const EntityId OldId, - const EntityLinkage &EL); + const EntityLinkage &Linkage); llvm::Error merge(std::map<SummaryName, @@ -53,7 +53,8 @@ class EntityLinker { std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>> &OutputData, - const EntityId OldId, const EntityId NewId, const EntityLinkage &EL, + const EntityId OldId, const EntityId NewId, + const EntityLinkage &Linkage, std::vector<EntitySummaryEncoding *> &PatchTargets); void patch(std::vector<EntitySummaryEncoding *> &PatchTargets, diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h index af775769dc1e4..a0014f0039f61 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h @@ -9,6 +9,8 @@ #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H #define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H +#include "llvm/ADT/StringRef.h" + namespace clang::ssaf { /// Represents the linkage properties of an entity in the program model. @@ -34,6 +36,9 @@ class EntityLinkage { friend class SerializationFormat; }; +/// Returns a string representation of the linkage type. +llvm::StringRef toString(EntityLinkage::LinkageType Linkage); + } // namespace clang::ssaf #endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt index 8ef1625cc704b..7e4404d96bbd4 100644 --- a/clang/lib/Analysis/Scalable/CMakeLists.txt +++ b/clang/lib/Analysis/Scalable/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangAnalysisScalable EntityLinker/EntityLinker.cpp Model/BuildNamespace.cpp Model/EntityIdTable.cpp + Model/EntityLinkage.cpp Model/EntityName.cpp Serialization/JSONFormat.cpp Serialization/SerializationFormatRegistry.cpp diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp index 2c7344b19de55..9b15181721566 100644 --- a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp +++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp @@ -8,11 +8,9 @@ #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; @@ -37,8 +35,6 @@ constexpr const char *MissingLinkageInformation = 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"; @@ -63,9 +59,9 @@ resolveNamespace(const NestedBuildNamespace &LUNamespace, llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName, const EntityId OldId, - const EntityLinkage &EL) { - NestedBuildNamespace NewNamespace = - resolveNamespace(Output.LUNamespace, OldName.Namespace, EL.getLinkage()); + const EntityLinkage &Linkage) { + NestedBuildNamespace NewNamespace = resolveNamespace( + Output.LUNamespace, OldName.Namespace, Linkage.getLinkage()); EntityName NewName(OldName.USR, OldName.Suffix, NewNamespace); @@ -75,7 +71,7 @@ llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName, // 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); + auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, Linkage); if (!Inserted) { return ErrorBuilder::create( llvm::inconvertibleErrorCode(), @@ -93,7 +89,7 @@ llvm::Error EntityLinker::merge( std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>> &OutputData, - const EntityId OldId, const EntityId NewId, const EntityLinkage &EL, + const EntityId OldId, const EntityId NewId, const EntityLinkage &Linkage, std::vector<EntitySummaryEncoding *> &PatchTargets) { for (auto &[Name, DataMap] : InputData) { auto Iter = DataMap.find(OldId); @@ -101,26 +97,23 @@ llvm::Error EntityLinker::merge( continue; auto &OutputMap = OutputData[Name]; - auto Result = OutputMap.insert({NewId, std::move(Iter->second)}); + auto InsertResult = 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()); + if (InsertResult.second) { + PatchTargets.push_back(InsertResult.first->second.get()); } else { - switch (EL.getLinkage()) { + switch (Linkage.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") + OldId.Index, NewId.Index, + toString(Linkage.getLinkage())) .build(); case EntityLinkage::LinkageType::External: // Insertion is expected to fail for duplicate occurrences of `External` diff --git a/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp b/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp new file mode 100644 index 0000000000000..b1f50964969c3 --- /dev/null +++ b/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp @@ -0,0 +1,27 @@ +//===- EntityLinkage.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/Model/EntityLinkage.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang::ssaf { + +llvm::StringRef toString(EntityLinkage::LinkageType Linkage) { + switch (Linkage) { + case EntityLinkage::LinkageType::None: + return "None"; + case EntityLinkage::LinkageType::Internal: + return "Internal"; + case EntityLinkage::LinkageType::External: + return "External"; + } + + llvm_unreachable("Unhandled EntityLinkage::LinkageType variant"); +} + +} // namespace clang::ssaf _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
