https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/206148
>From 53a7fcfe0ce7460f02584a39cef072db095c6da3 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Tue, 30 Jun 2026 09:12:38 -0700 Subject: [PATCH] Add MultiArchStaticLibrary --- .../EntityLinker/MultiArchStaticLibrary.h | 62 ++++++++ .../Core/EntityLinker/StaticLibrary.h | 1 + .../Core/Model/PrivateFieldNames.def | 1 + .../Core/Serialization/JSONFormat.h | 16 +++ .../Core/Serialization/SerializationFormat.h | 19 ++- .../Core/CMakeLists.txt | 1 + .../Serialization/JSONFormat/Artifact.cpp | 26 +++- .../Serialization/JSONFormat/JSONFormatImpl.h | 8 +- .../JSONFormat/MultiArchStaticLibrary.cpp | 134 ++++++++++++++++++ .../JSONFormat/StaticLibrary.cpp | 18 ++- .../rt-multi-arch-static-library-empty.json | 14 ++ ...rt-multi-arch-static-library-nonempty.json | 35 +++++ .../ssaf-format/Artifact/round-trip.test | 13 ++ .../ssaf-format/Artifact/top-level.test | 2 +- .../Inputs/duplicate-target-triple.json | 23 +++ .../Inputs/invalid-syntax.json | 1 + .../Inputs/member-inner-error.json | 10 ++ .../Inputs/member-mismatched-type.json | 14 ++ .../Inputs/member-missing-type.json | 13 ++ .../Inputs/member-not-object.json | 6 + .../Inputs/members-not-array.json | 4 + .../Inputs/mismatched-type.json | 14 ++ .../Inputs/missing-members.json | 3 + .../Inputs/missing-type.json | 13 ++ .../Inputs/not-json-extension.txt | 1 + .../Inputs/not-object.json | 1 + .../Inputs/rt-empty-members.json | 4 + .../Inputs/rt-multiple-members.json | 23 +++ .../Inputs/rt-nonempty-members.json | 47 ++++++ .../Inputs/rt-single-member.json | 14 ++ .../Inputs/rt-three-members.json | 32 +++++ .../Inputs/unsorted-members-input.json | 23 +++ .../Inputs/unsorted-three-members-input.json | 32 +++++ .../MultiArchStaticLibrary/io.test | 49 +++++++ .../MultiArchStaticLibrary/permissions.test | 40 ++++++ .../MultiArchStaticLibrary/round-trip.test | 55 +++++++ .../MultiArchStaticLibrary/top-level.test | 71 ++++++++++ clang/tools/clang-ssaf-format/SSAFFormat.cpp | 20 ++- .../TUSummaryExtractorFrontendActionTest.cpp | 18 +++ .../Registries/MockSerializationFormat.cpp | 13 ++ .../Registries/MockSerializationFormat.h | 6 + .../ScalableStaticAnalysis/TestFixture.h | 1 + 42 files changed, 879 insertions(+), 22 deletions(-) create mode 100644 clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h create mode 100644 clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/MultiArchStaticLibrary.cpp create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-empty.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-nonempty.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/duplicate-target-triple.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/invalid-syntax.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-inner-error.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-mismatched-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-missing-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-not-object.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/members-not-array.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/mismatched-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-json-extension.txt create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-object.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-empty-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-multiple-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-nonempty-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-single-member.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-three-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-members-input.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-three-members-input.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/io.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/permissions.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/round-trip.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/top-level.test diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h new file mode 100644 index 0000000000000..ef64c8b665e5e --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h @@ -0,0 +1,62 @@ +//===- MultiArchStaticLibrary.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 MultiArchStaticLibrary class, which represents a +// multi-architecture wrapper around per-architecture StaticLibrary +// instances (the SSAF analogue of a Mach-O fat static library, e.g. one +// produced by `lipo -create` over per-arch `.a` files). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_MULTIARCHSTATICLIBRARY_H +#define LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_MULTIARCHSTATICLIBRARY_H + +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" +#include <memory> +#include <set> +#include <tuple> + +namespace clang::ssaf { + +/// Represents a multi-architecture static library. +/// +/// A MultiArchStaticLibrary bundles per-architecture StaticLibrary +/// members, mirroring the role of a Mach-O fat static library (the result +/// of `lipo -create` over per-architecture `.a` files). +class MultiArchStaticLibrary { + friend class SerializationFormat; + friend class TestFixture; + + /// Orders members by their TargetTriple's canonical enum components. + /// llvm::Triple's parser maps alias spellings to the same enum values + /// (e.g. "aarch64" and "arm64" both become Triple::aarch64), so a + /// tuple-of-enums compare is equivalent to comparing normalized triples + /// for identity, but without the per-call string allocation. + struct MemberByTargetTriple { + static auto key(const llvm::Triple &T) { + return std::make_tuple(T.getArch(), T.getSubArch(), T.getVendor(), + T.getOS(), T.getEnvironment(), + T.getObjectFormat()); + } + bool operator()(const std::unique_ptr<StaticLibrary> &A, + const std::unique_ptr<StaticLibrary> &B) const { + return key(A->TargetTriple) < key(B->TargetTriple); + } + }; + + // StaticLibrary objects ordered by TargetTriple enum components. Two + // members with the same TargetTriple are not permitted. + std::set<std::unique_ptr<StaticLibrary>, MemberByTargetTriple> Members; + +public: + MultiArchStaticLibrary() = default; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_MULTIARCHSTATICLIBRARY_H diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h index ae8cfa4c7bacb..4ed34c9ec68cf 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h @@ -39,6 +39,7 @@ namespace clang::ssaf { /// tool never decodes per-entity payloads, and the linker consumes them /// as-is during its selective inclusion pass. class StaticLibrary { + friend class MultiArchStaticLibrary; friend class SerializationFormat; friend class TestFixture; diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def index b1c37823dc3fe..7777b0adb4a06 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def @@ -35,6 +35,7 @@ FIELD(LUSummaryEncoding, Data) FIELD(LUSummaryEncoding, IdTable) FIELD(LUSummaryEncoding, LinkageTable) FIELD(LUSummaryEncoding, LUNamespace) +FIELD(MultiArchStaticLibrary, Members) FIELD(NestedBuildNamespace, Namespaces) FIELD(StaticLibrary, Members) FIELD(StaticLibrary, Namespace) diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h index d4b6cfc0e1690..094628ab7fa04 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h @@ -74,6 +74,12 @@ class JSONFormat final : public SerializationFormat { llvm::Error writeStaticLibrary(const StaticLibrary &S, llvm::StringRef Path) override; + llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibrary(llvm::StringRef Path) override; + + llvm::Error writeMultiArchStaticLibrary(const MultiArchStaticLibrary &M, + llvm::StringRef Path) override; + llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override; llvm::Error writeWPASuite(const WPASuite &Suite, @@ -132,11 +138,21 @@ class JSONFormat final : public SerializationFormat { /// See \c readTUSummaryFromObject for caller responsibilities. llvm::Expected<StaticLibrary> readStaticLibraryFromObject(const Object &Root); + /// Parses a MultiArchStaticLibrary from an already-validated root JSON + /// object. See \c readTUSummaryFromObject for caller responsibilities. + llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibraryFromObject(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; + /// Serializes a StaticLibrary to a JSON object including its + /// self-describing \c type field. Used both by \c writeStaticLibrary + /// and by the MultiArchStaticLibrary writer to emit per-arch slices. + Object staticLibraryToJSON(const StaticLibrary &S) 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 29b662bc0e380..a9c25de95a598 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/MultiArchStaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" @@ -42,11 +43,12 @@ using Artifact = std::variant<TUSummary, LUSummary, WPASuite>; /// artifacts but with their per-entity summary payloads left as opaque /// format-specific encodings rather than fully resolved analysis results. /// -/// \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>; +/// \c StaticLibrary and \c MultiArchStaticLibrary appear only in this +/// variant: the archiver/arch tools and the linker pass member payloads +/// through without decoding them, so fully decoded shapes would have no +/// consumer. +using ArtifactEncoding = std::variant<TUSummaryEncoding, LUSummaryEncoding, + StaticLibrary, MultiArchStaticLibrary>; /// Abstract base class for serialization formats. class SerializationFormat { @@ -106,6 +108,13 @@ class SerializationFormat { virtual llvm::Error writeStaticLibrary(const StaticLibrary &S, llvm::StringRef Path) = 0; + virtual llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibrary(llvm::StringRef Path) = 0; + + virtual llvm::Error + writeMultiArchStaticLibrary(const MultiArchStaticLibrary &M, + 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 0a0ce19d63732..47f018f61becf 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/MultiArchStaticLibrary.cpp Serialization/JSONFormat/StaticLibrary.cpp Serialization/JSONFormat/TUSummary.cpp Serialization/JSONFormat/TUSummaryEncoding.cpp diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp index e5fc46a8ea952..a34f5cb718ab2 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp @@ -155,11 +155,21 @@ JSONFormat::readArtifactEncoding(llvm::StringRef Path) { return ArtifactEncoding{std::move(*ExpectedStaticLibrary)}; } - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::UnknownArtifactEncodingType, - *ExpectedType, JSONTypeKey, - JSONTypeValueTUSummary, JSONTypeValueLUSummary, - JSONTypeValueStaticLibrary) + if (*ExpectedType == JSONTypeValueMultiArchStaticLibrary) { + auto ExpectedM = readMultiArchStaticLibraryFromObject(*RootObjectPtr); + if (!ExpectedM) { + return ErrorBuilder::wrap(ExpectedM.takeError()) + .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path) + .build(); + } + return ArtifactEncoding{std::move(*ExpectedM)}; + } + + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::UnknownArtifactEncodingType, *ExpectedType, + JSONTypeKey, JSONTypeValueTUSummary, JSONTypeValueLUSummary, + JSONTypeValueStaticLibrary, JSONTypeValueMultiArchStaticLibrary) .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path) .build(); } @@ -173,11 +183,13 @@ llvm::Error JSONFormat::writeArtifactEncoding(const ArtifactEncoding &E, return writeTUSummaryEncoding(Enc, Path); } else if constexpr (std::is_same_v<T, LUSummaryEncoding>) { return writeLUSummaryEncoding(Enc, Path); + } else if constexpr (std::is_same_v<T, StaticLibrary>) { + return writeStaticLibrary(Enc, Path); } else { static_assert( - std::is_same_v<T, StaticLibrary>, + std::is_same_v<T, MultiArchStaticLibrary>, "ArtifactEncoding visitor must cover all variant alternatives"); - return writeStaticLibrary(Enc, Path); + return writeMultiArchStaticLibrary(Enc, Path); } }, E); diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h index e83a8dabac113..9cfe67e20d1d7 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h @@ -85,7 +85,8 @@ 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}', '{3}', or '{4}'"; + "unknown value '{0}' for field '{1}': expected '{2}', '{3}', '{4}', or " + "'{5}'"; inline constexpr const char *FailedToDeserializeEntitySummaryNoFormatInfo = "failed to deserialize EntitySummary: no FormatInfo registered for '{0}'"; @@ -152,6 +153,11 @@ 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 MultiArchStaticLibrary +/// files. +inline constexpr const char *JSONTypeValueMultiArchStaticLibrary = + "MultiArchStaticLibrary"; + /// 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/MultiArchStaticLibrary.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/MultiArchStaticLibrary.cpp new file mode 100644 index 0000000000000..810924944a4ee --- /dev/null +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/MultiArchStaticLibrary.cpp @@ -0,0 +1,134 @@ +//===- MultiArchStaticLibrary.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/MultiArchStaticLibrary.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" +#include "llvm/TargetParser/Triple.h" + +namespace clang::ssaf { + +//---------------------------------------------------------------------------- +// MultiArchStaticLibrary +//---------------------------------------------------------------------------- + +llvm::Expected<MultiArchStaticLibrary> +JSONFormat::readMultiArchStaticLibrary(llvm::StringRef Path) { + auto ExpectedJSON = readJSON(Path); + if (!ExpectedJSON) { + return ErrorBuilder::wrap(ExpectedJSON.takeError()) + .context(ErrorMessages::ReadingFromFile, "MultiArchStaticLibrary", Path) + .build(); + } + + Object *RootObjectPtr = ExpectedJSON->getAsObject(); + if (!RootObjectPtr) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObject, + "MultiArchStaticLibrary", "object") + .context(ErrorMessages::ReadingFromFile, "MultiArchStaticLibrary", Path) + .build(); + } + + if (auto Err = checkSummaryType(*RootObjectPtr, + JSONTypeValueMultiArchStaticLibrary)) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::ReadingFromFile, "MultiArchStaticLibrary", Path) + .build(); + } + + auto ExpectedM = readMultiArchStaticLibraryFromObject(*RootObjectPtr); + if (!ExpectedM) { + return ErrorBuilder::wrap(ExpectedM.takeError()) + .context(ErrorMessages::ReadingFromFile, "MultiArchStaticLibrary", Path) + .build(); + } + + return std::move(*ExpectedM); +} + +llvm::Expected<MultiArchStaticLibrary> +JSONFormat::readMultiArchStaticLibraryFromObject(const Object &RootObject) { + const Array *MembersArray = RootObject.getArray("members"); + if (!MembersArray) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "MultiArchStaticLibrary members", "members", + "array") + .build(); + } + + MultiArchStaticLibrary M; + auto &Members = getMembers(M); + + for (const auto &[Index, MemberValue] : llvm::enumerate(*MembersArray)) { + const Object *MemberObject = MemberValue.getAsObject(); + if (!MemberObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtIndex, + "MultiArchStaticLibrary member", Index, + "object") + .build(); + } + + if (auto Err = + checkSummaryType(*MemberObject, JSONTypeValueStaticLibrary)) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::ReadingFromIndex, + "MultiArchStaticLibrary member", Index) + .build(); + } + + auto ExpectedMember = readStaticLibraryFromObject(*MemberObject); + if (!ExpectedMember) { + return ErrorBuilder::wrap(ExpectedMember.takeError()) + .context(ErrorMessages::ReadingFromIndex, + "MultiArchStaticLibrary member", Index) + .build(); + } + + auto [It, Inserted] = Members.insert( + std::make_unique<StaticLibrary>(std::move(*ExpectedMember))); + if (!Inserted) { + auto MemberTriple = llvm::Triple::normalize(getTargetTriple(**It).str()); + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedInsertionOnDuplication, + "MultiArchStaticLibrary member", Index, + MemberTriple) + .build(); + } + } + + return std::move(M); +} + +llvm::Error +JSONFormat::writeMultiArchStaticLibrary(const MultiArchStaticLibrary &M, + llvm::StringRef Path) { + Object RootObject; + + RootObject[JSONTypeKey] = JSONTypeValueMultiArchStaticLibrary; + + Array MembersArray; + MembersArray.reserve(getMembers(M).size()); + for (const auto &Member : getMembers(M)) { + MembersArray.push_back(staticLibraryToJSON(*Member)); + } + RootObject["members"] = std::move(MembersArray); + + if (auto Error = writeJSON(std::move(RootObject), Path)) { + return ErrorBuilder::wrap(std::move(Error)) + .context(ErrorMessages::WritingToFile, "MultiArchStaticLibrary", Path) + .build(); + } + + return llvm::Error::success(); +} + +} // namespace clang::ssaf diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp index b34c63b185b0e..d4538d19aed24 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp @@ -164,6 +164,16 @@ JSONFormat::readStaticLibraryFromObject(const Object &RootObject) { llvm::Error JSONFormat::writeStaticLibrary(const StaticLibrary &S, llvm::StringRef Path) { + if (auto Error = writeJSON(staticLibraryToJSON(S), Path)) { + return ErrorBuilder::wrap(std::move(Error)) + .context(ErrorMessages::WritingToFile, "StaticLibrary", Path) + .build(); + } + + return llvm::Error::success(); +} + +Object JSONFormat::staticLibraryToJSON(const StaticLibrary &S) const { Object RootObject; RootObject[JSONTypeKey] = JSONTypeValueStaticLibrary; @@ -180,13 +190,7 @@ llvm::Error JSONFormat::writeStaticLibrary(const StaticLibrary &S, } 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(); + return RootObject; } } // namespace clang::ssaf diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-empty.json b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-empty.json new file mode 100644 index 0000000000000..7b361e52946e1 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-empty.json @@ -0,0 +1,14 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-nonempty.json b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-nonempty.json new file mode 100644 index 0000000000000..10486cebf75be --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-multi-arch-static-library-nonempty.json @@ -0,0 +1,35 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "x86_64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} 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 d6d67826e4847..32214cabbc36b 100644 --- a/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test @@ -47,3 +47,16 @@ // 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 + +// RUN: clang-ssaf-format --encoding %S/Inputs/rt-multi-arch-static-library-empty.json -o %t/rt-multi-arch-static-library-empty-enc.json +// RUN: diff %S/Inputs/rt-multi-arch-static-library-empty.json %t/rt-multi-arch-static-library-empty-enc.json +// RUN: clang-ssaf-format --type auto --encoding %S/Inputs/rt-multi-arch-static-library-empty.json -o %t/rt-multi-arch-static-library-empty-enc-explicit.json +// RUN: diff %S/Inputs/rt-multi-arch-static-library-empty.json %t/rt-multi-arch-static-library-empty-enc-explicit.json + +// Non-empty MultiArchStaticLibrary routed through the auto-mode --encoding +// dispatcher exercises both arch-slice writers and the inner TUSummaryEncoding +// path inside the StaticLibrary member. +// RUN: clang-ssaf-format --encoding %S/Inputs/rt-multi-arch-static-library-nonempty.json -o %t/rt-multi-arch-static-library-nonempty-enc.json +// RUN: diff %S/Inputs/rt-multi-arch-static-library-nonempty.json %t/rt-multi-arch-static-library-nonempty-enc.json +// RUN: clang-ssaf-format --type auto --encoding %S/Inputs/rt-multi-arch-static-library-nonempty.json -o %t/rt-multi-arch-static-library-nonempty-enc-explicit.json +// RUN: diff %S/Inputs/rt-multi-arch-static-library-nonempty.json %t/rt-multi-arch-static-library-nonempty-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 7838f3a90534c..5f8333ca52b37 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', 'LUSummary', or 'StaticLibrary' +// ENCODING-UNKNOWN-TYPE-NEXT: unknown value 'Mystery' for field 'type': expected 'TUSummary', 'LUSummary', 'StaticLibrary', or 'MultiArchStaticLibrary' diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/duplicate-target-triple.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/duplicate-target-triple.json new file mode 100644 index 0000000000000..af94a33d2eff0 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/duplicate-target-triple.json @@ -0,0 +1,23 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/invalid-syntax.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/invalid-syntax.json new file mode 100644 index 0000000000000..b0e13f61aa06e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/invalid-syntax.json @@ -0,0 +1 @@ +{ invalid json } diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-inner-error.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-inner-error.json new file mode 100644 index 0000000000000..8f06d662e6994 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-inner-error.json @@ -0,0 +1,10 @@ +{ + "members": [ + { + "members": [], + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-mismatched-type.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-mismatched-type.json new file mode 100644 index 0000000000000..8680c6e7aec1d --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-mismatched-type.json @@ -0,0 +1,14 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "TUSummary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-missing-type.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-missing-type.json new file mode 100644 index 0000000000000..6779f9b89ae2f --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-missing-type.json @@ -0,0 +1,13 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-not-object.json new file mode 100644 index 0000000000000..8756cf45a716e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/member-not-object.json @@ -0,0 +1,6 @@ +{ + "members": [ + "not an object" + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/members-not-array.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/members-not-array.json new file mode 100644 index 0000000000000..8d4a18c4333d8 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/members-not-array.json @@ -0,0 +1,4 @@ +{ + "members": {}, + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/mismatched-type.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/mismatched-type.json new file mode 100644 index 0000000000000..390eb2a4cb0a9 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/mismatched-type.json @@ -0,0 +1,14 @@ +{ + "members": { + "arm64": { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + }, + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-members.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-members.json new file mode 100644 index 0000000000000..245015c536185 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-members.json @@ -0,0 +1,3 @@ +{ + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-type.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-type.json new file mode 100644 index 0000000000000..ea946be0839aa --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/missing-type.json @@ -0,0 +1,13 @@ +{ + "members": { + "arm64": { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + } +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-json-extension.txt b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-json-extension.txt new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-json-extension.txt @@ -0,0 +1 @@ +{} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-object.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-object.json new file mode 100644 index 0000000000000..fe51488c7066f --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/not-object.json @@ -0,0 +1 @@ +[] diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-empty-members.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-empty-members.json new file mode 100644 index 0000000000000..a04e97923e36d --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-empty-members.json @@ -0,0 +1,4 @@ +{ + "members": [], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-multiple-members.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-multiple-members.json new file mode 100644 index 0000000000000..aeaa4dc04a3ca --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-multiple-members.json @@ -0,0 +1,23 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-nonempty-members.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-nonempty-members.json new file mode 100644 index 0000000000000..91f89e3b4c6f8 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-nonempty-members.json @@ -0,0 +1,47 @@ +{ + "members": [ + { + "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" + }, + { + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "x86_64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-single-member.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-single-member.json new file mode 100644 index 0000000000000..7b361e52946e1 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-single-member.json @@ -0,0 +1,14 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-three-members.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-three-members.json new file mode 100644 index 0000000000000..f64ae43057bdf --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/rt-three-members.json @@ -0,0 +1,32 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64_32-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-members-input.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-members-input.json new file mode 100644 index 0000000000000..298f4817861d1 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-members-input.json @@ -0,0 +1,23 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-three-members-input.json b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-three-members-input.json new file mode 100644 index 0000000000000..fec759645e966 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/Inputs/unsorted-three-members-input.json @@ -0,0 +1,32 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64_32-apple-macosx", + "type": "StaticLibrary" + } + ], + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/io.test b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/io.test new file mode 100644 index 0000000000000..56f5bdd2021c0 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/io.test @@ -0,0 +1,49 @@ +// File-I/O error tests: readJSON() input validation and writeJSON() output +// validation for clang-ssaf-format --type multi-arch-static-library. + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// ============================================================================ +// readJSON() errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/nonexistent.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NONEXISTENT-FILE +// NONEXISTENT-FILE: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent.json': Path does not exist + +// RUN: mkdir -p %t/test-directory.json +// RUN: not clang-ssaf-format --type multi-arch-static-library %t/test-directory.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=PATH-IS-DIRECTORY +// PATH-IS-DIRECTORY: clang-ssaf-format: error: failed to validate path '{{.*}}test-directory.json': Path is not a file + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/not-json-extension.txt 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NOT-JSON-EXTENSION +// NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}not-json-extension.txt': No format registered for extension 'txt' + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/invalid-syntax.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=INVALID-SYNTAX +// INVALID-SYNTAX: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}invalid-syntax.json' +// INVALID-SYNTAX-NEXT: {{.*}}Expected object key + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/not-object.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NOT-OBJECT +// NOT-OBJECT: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}not-object.json' +// NOT-OBJECT-NEXT: failed to read MultiArchStaticLibrary: expected JSON object + +// ============================================================================ +// writeJSON() errors +// ============================================================================ + +// RUN: echo '{}' > %t/existing.json +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-single-member.json -o %t/existing.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-FILE-ALREADY-EXISTS +// WRITE-FILE-ALREADY-EXISTS: clang-ssaf-format: error: failed to validate path '{{.*}}existing.json': File already exists + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-single-member.json -o %t/nonexistent-dir/test.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-PARENT-DIRECTORY-NOT-FOUND +// WRITE-PARENT-DIRECTORY-NOT-FOUND: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent-dir{{.}}test.json': Parent directory does not exist + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-single-member.json -o %t/test.txt 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-NOT-JSON-EXTENSION +// WRITE-NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}test.txt': No format registered for extension 'txt' diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/permissions.test b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/permissions.test new file mode 100644 index 0000000000000..821099842135b --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/permissions.test @@ -0,0 +1,40 @@ +// Tests for clang-ssaf-format --type multi-arch-static-library that +// require filesystem permission support (symlinks, chmod). + +// UNSUPPORTED: system-windows +// REQUIRES: non-root-user + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// ============================================================================ +// Broken symlink +// ============================================================================ + +// RUN: ln -sf %t/nonexistent-target.json %t/broken-symlink.json +// RUN: not clang-ssaf-format --type multi-arch-static-library %t/broken-symlink.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=BROKEN-SYMLINK +// BROKEN-SYMLINK: clang-ssaf-format: error: failed to validate path '{{.*}}broken-symlink.json': Path does not exist + +// ============================================================================ +// No read permission +// ============================================================================ + +// RUN: cp %S/Inputs/rt-single-member.json %t/no-read-permission.json +// RUN: chmod -r %t/no-read-permission.json +// RUN: not clang-ssaf-format --type multi-arch-static-library %t/no-read-permission.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NO-READ-PERMISSION +// RUN: chmod +r %t/no-read-permission.json +// NO-READ-PERMISSION: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}no-read-permission.json' +// NO-READ-PERMISSION-NEXT: failed to read file '{{.*}}no-read-permission.json': {{.*}} + +// ============================================================================ +// Write to directory without write permission +// ============================================================================ + +// RUN: mkdir -p %t/write-protected-dir +// RUN: chmod -w %t/write-protected-dir +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-single-member.json -o %t/write-protected-dir/test.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-STREAM-OPEN-FAILURE +// RUN: chmod +w %t/write-protected-dir +// WRITE-STREAM-OPEN-FAILURE: clang-ssaf-format: error: failed to validate path '{{.*}}write-protected-dir{{.}}test.json': Parent directory is not writable diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/round-trip.test b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/round-trip.test new file mode 100644 index 0000000000000..502ed54d8f1f1 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/round-trip.test @@ -0,0 +1,55 @@ +// Round-trip tests: read a MultiArchStaticLibrary JSON input, write it +// back out, and diff the result against the original. +// +// MultiArchStaticLibrary has only an encoding representation, so +// --encoding is a no-op for --type multi-arch-static-library and both +// invocations must produce the same output. + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// An empty members array is a valid (if degenerate) MultiArchStaticLibrary. +// RUN: clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-empty-members.json -o %t/rt-empty-members.json +// RUN: diff %S/Inputs/rt-empty-members.json %t/rt-empty-members.json +// RUN: clang-ssaf-format --type multi-arch-static-library --encoding %S/Inputs/rt-empty-members.json -o %t/rt-empty-members-enc.json +// RUN: diff %S/Inputs/rt-empty-members.json %t/rt-empty-members-enc.json + +// RUN: clang-ssaf-format --type multi-arch-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 multi-arch-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 multi-arch-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 multi-arch-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 + +// RUN: clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-nonempty-members.json -o %t/rt-nonempty-members.json +// RUN: diff %S/Inputs/rt-nonempty-members.json %t/rt-nonempty-members.json +// RUN: clang-ssaf-format --type multi-arch-static-library --encoding %S/Inputs/rt-nonempty-members.json -o %t/rt-nonempty-members-enc.json +// RUN: diff %S/Inputs/rt-nonempty-members.json %t/rt-nonempty-members-enc.json + +// ============================================================================ +// Member sort order +// ============================================================================ +// +// Members are stored in a std::set keyed by TargetTriple enum components, +// so the writer emits them in enum-tuple order regardless of input order. +// unsorted-members-input.json lists the members as x86_64, then arm64; +// the writer must produce the canonical (arm64, x86_64) ordering, byte +// identical to rt-multiple-members.json. + +// RUN: clang-ssaf-format --type multi-arch-static-library %S/Inputs/unsorted-members-input.json -o %t/unsorted-members-output.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/unsorted-members-output.json +// RUN: clang-ssaf-format --type multi-arch-static-library --encoding %S/Inputs/unsorted-members-input.json -o %t/unsorted-members-output-enc.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/unsorted-members-output-enc.json + +// 3-member ordering: confirms the sort isn't an accidental two-element +// swap. The unsorted input lists x86_64, arm64, arm64_32; the writer +// must produce (arm64, arm64_32, x86_64) — byte identical to +// rt-three-members.json. + +// RUN: clang-ssaf-format --type multi-arch-static-library %S/Inputs/rt-three-members.json -o %t/rt-three-members.json +// RUN: diff %S/Inputs/rt-three-members.json %t/rt-three-members.json +// RUN: clang-ssaf-format --type multi-arch-static-library %S/Inputs/unsorted-three-members-input.json -o %t/unsorted-three-members-output.json +// RUN: diff %S/Inputs/rt-three-members.json %t/unsorted-three-members-output.json diff --git a/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/top-level.test b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/top-level.test new file mode 100644 index 0000000000000..d46e251485f75 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/MultiArchStaticLibrary/top-level.test @@ -0,0 +1,71 @@ +// Top-level MultiArchStaticLibrary structure tests: type discriminator, +// members array, per-member validation including duplicate-target-triple +// rejection. + +// ============================================================================ +// readMultiArchStaticLibrary() / writeMultiArchStaticLibrary() type-field +// errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type multi-arch-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 MultiArchStaticLibrary 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 multi-arch-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 MultiArchStaticLibrary from file '{{.*}}mismatched-type.json' +// MISMATCHED-TYPE-NEXT: expected 'MultiArchStaticLibrary' for field 'type' but got 'StaticLibrary' + +// ============================================================================ +// members field errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type multi-arch-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 MultiArchStaticLibrary from file '{{.*}}missing-members.json' +// MISSING-MEMBERS-NEXT: failed to read MultiArchStaticLibrary members from field 'members': expected JSON array + +// The members field must be a JSON array, not an object. +// RUN: not clang-ssaf-format --type multi-arch-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 MultiArchStaticLibrary from file '{{.*}}members-not-array.json' +// MEMBERS-NOT-ARRAY-NEXT: failed to read MultiArchStaticLibrary members from field 'members': expected JSON array + +// ============================================================================ +// per-member errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type multi-arch-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 MultiArchStaticLibrary from file '{{.*}}member-not-object.json' +// MEMBER-NOT-OBJECT-NEXT: failed to read MultiArchStaticLibrary member from index '0': expected JSON object + +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/member-missing-type.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-MISSING-TYPE +// MEMBER-MISSING-TYPE: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}member-missing-type.json' +// MEMBER-MISSING-TYPE-NEXT: reading MultiArchStaticLibrary member from index '0' +// MEMBER-MISSING-TYPE-NEXT: failed to read summary type from field 'type': expected JSON string + +// RUN: not clang-ssaf-format --type multi-arch-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 MultiArchStaticLibrary from file '{{.*}}member-mismatched-type.json' +// MEMBER-MISMATCHED-TYPE-NEXT: reading MultiArchStaticLibrary member from index '0' +// MEMBER-MISMATCHED-TYPE-NEXT: expected 'StaticLibrary' for field 'type' but got 'TUSummary' + +// Members are identified by their full target_triple. Two members sharing +// the same target_triple are rejected. +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/duplicate-target-triple.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-TARGET-TRIPLE +// DUPLICATE-TARGET-TRIPLE: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}duplicate-target-triple.json' +// DUPLICATE-TARGET-TRIPLE-NEXT: failed to insert MultiArchStaticLibrary member at index '1': encountered duplicate 'arm64-apple-macosx' + +// Verifies that errors raised inside readStaticLibraryFromObject (here, a +// missing namespace on the member) are properly wrapped with the +// "reading MultiArchStaticLibrary member from index 'N'" context, then +// with the outer "reading MultiArchStaticLibrary from file '...'" context. +// RUN: not clang-ssaf-format --type multi-arch-static-library %S/Inputs/member-inner-error.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-INNER-ERROR +// MEMBER-INNER-ERROR: clang-ssaf-format: error: reading MultiArchStaticLibrary from file '{{.*}}member-inner-error.json' +// MEMBER-INNER-ERROR-NEXT: reading MultiArchStaticLibrary member from index '0' +// MEMBER-INNER-ERROR-NEXT: failed to read BuildNamespace from field 'namespace': expected JSON object diff --git a/clang/tools/clang-ssaf-format/SSAFFormat.cpp b/clang/tools/clang-ssaf-format/SSAFFormat.cpp index 5b94da384cb7d..b0f123f75be2a 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/MultiArchStaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h" @@ -40,7 +41,14 @@ namespace { // Summary Type //===----------------------------------------------------------------------===// -enum class SummaryType { Auto, TU, LU, StaticLibrary, WPA }; +enum class SummaryType { + Auto, + TU, + LU, + StaticLibrary, + MultiArchStaticLibrary, + WPA +}; //===----------------------------------------------------------------------===// // Command-Line Options @@ -66,6 +74,9 @@ cl::opt<SummaryType> Type( clEnumValN(SummaryType::LU, "lu", "Link unit summary"), clEnumValN(SummaryType::StaticLibrary, "static-library", "Static library of translation unit summaries"), + clEnumValN(SummaryType::MultiArchStaticLibrary, + "multi-arch-static-library", + "Multi-architecture static library"), clEnumValN(SummaryType::WPA, "wpa", "Whole-program analysis suite")), cl::init(SummaryType::Auto), cl::cat(SsafFormatCategory)); @@ -304,6 +315,13 @@ void convert(const FormatInput &FI) { run(FI, &SerializationFormat::readStaticLibrary, &SerializationFormat::writeStaticLibrary); return; + case SummaryType::MultiArchStaticLibrary: + // MultiArchStaticLibrary has only an encoded representation, so + // --encoding is a no-op here: both paths route to + // readMultiArchStaticLibrary / writeMultiArchStaticLibrary. + run(FI, &SerializationFormat::readMultiArchStaticLibrary, + &SerializationFormat::writeMultiArchStaticLibrary); + return; case SummaryType::WPA: run(FI, &SerializationFormat::readWPASuite, &SerializationFormat::writeWPASuite); diff --git a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp index 4baf593f57eef..79504a4f9754f 100644 --- a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp +++ b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp @@ -108,6 +108,16 @@ class FailingSerializationFormat final : public SerializationFormat { return failing("writeStaticLibrary"); } + llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibrary(llvm::StringRef Path) override { + return failing("readMultiArchStaticLibrary"); + } + + llvm::Error writeMultiArchStaticLibrary(const MultiArchStaticLibrary &M, + llvm::StringRef Path) override { + return failing("writeMultiArchStaticLibrary"); + } + llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override { return failing("readWPASuite"); } @@ -196,6 +206,14 @@ class CapturingSerializationFormat final : public SerializationFormat { llvm::StringRef) override { return llvm::Error::success(); } + llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibrary(llvm::StringRef) override { + return llvm::createStringError("not implemented"); + } + llvm::Error writeMultiArchStaticLibrary(const MultiArchStaticLibrary &, + 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/Registries/MockSerializationFormat.cpp b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp index 4a01248b53ac7..92d6d0abc8936 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/MultiArchStaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" @@ -209,6 +210,18 @@ llvm::Error MockSerializationFormat::writeStaticLibrary(const StaticLibrary &S, llvm_unreachable("MockSerializationFormat does not support StaticLibrary"); } +llvm::Expected<MultiArchStaticLibrary> +MockSerializationFormat::readMultiArchStaticLibrary(llvm::StringRef Path) { + llvm_unreachable( + "MockSerializationFormat does not support MultiArchStaticLibrary"); +} + +llvm::Error MockSerializationFormat::writeMultiArchStaticLibrary( + const MultiArchStaticLibrary &M, llvm::StringRef Path) { + llvm_unreachable( + "MockSerializationFormat does not support MultiArchStaticLibrary"); +} + 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 0cadd0b98fe3a..e03e5092589fe 100644 --- a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h +++ b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h @@ -50,6 +50,12 @@ class MockSerializationFormat final : public SerializationFormat { llvm::Error writeStaticLibrary(const StaticLibrary &S, llvm::StringRef Path) override; + llvm::Expected<MultiArchStaticLibrary> + readMultiArchStaticLibrary(llvm::StringRef Path) override; + + llvm::Error writeMultiArchStaticLibrary(const MultiArchStaticLibrary &M, + 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/TestFixture.h b/clang/unittests/ScalableStaticAnalysis/TestFixture.h index e7eed940938ce..c2b7005bd4b0c 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/MultiArchStaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
