[llvm-branch-commits] [llvm] [TextAPI] Add support to convert RecordSlices -> InterfaceFile (PR #75007)

Cyndy Ishida via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Dec 11 09:30:46 PST 2023


https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/75007

>From d5e97577651b558b56de64f9ed74cd7893cea7ec Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Thu, 7 Dec 2023 13:57:17 -0800
Subject: [PATCH 1/2] [TextAPI] Add support to convert RecordSlices ->
 InterfaceFile

Introduce RecordVisitor. This is used for different clients that want to
extract information out of RecordSlice types.
The first and immediate usecase is for serializing symbol information
into TBD files.
---
 llvm/include/llvm/TextAPI/DylibReader.h   |   5 +
 llvm/include/llvm/TextAPI/Record.h        |   9 +-
 llvm/include/llvm/TextAPI/RecordVisitor.h |  54 ++++++++++
 llvm/include/llvm/TextAPI/RecordsSlice.h  |   6 +-
 llvm/lib/TextAPI/CMakeLists.txt           |   1 +
 llvm/lib/TextAPI/DylibReader.cpp          |  10 ++
 llvm/lib/TextAPI/RecordVisitor.cpp        |  67 ++++++++++++
 llvm/lib/TextAPI/RecordsSlice.cpp         | 124 +++++++++++++++++++++-
 8 files changed, 272 insertions(+), 4 deletions(-)
 create mode 100644 llvm/include/llvm/TextAPI/RecordVisitor.h
 create mode 100644 llvm/lib/TextAPI/RecordVisitor.cpp

diff --git a/llvm/include/llvm/TextAPI/DylibReader.h b/llvm/include/llvm/TextAPI/DylibReader.h
index aa98df99c99fa6..3d4ddb9745ddd4 100644
--- a/llvm/include/llvm/TextAPI/DylibReader.h
+++ b/llvm/include/llvm/TextAPI/DylibReader.h
@@ -42,6 +42,11 @@ struct ParseOption {
 /// \return List of record slices.
 Expected<Records> readFile(MemoryBufferRef Buffer, const ParseOption &Opt);
 
+/// Get TAPI file representation of binary dylib.
+///
+/// \param Buffer Data that points to dylib.
+Expected<std::unique_ptr<InterfaceFile>> get(MemoryBufferRef Buffer);
+
 } // namespace DylibReader
 
 } // end namespace MachO.
diff --git a/llvm/include/llvm/TextAPI/Record.h b/llvm/include/llvm/TextAPI/Record.h
index 5317982cbfa2bf..ec93098b320ce1 100644
--- a/llvm/include/llvm/TextAPI/Record.h
+++ b/llvm/include/llvm/TextAPI/Record.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_TEXTAPI_RECORD_H
 #define LLVM_TEXTAPI_RECORD_H
 
+#include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/TextAPI/Symbol.h"
@@ -50,7 +51,7 @@ class Record {
 public:
   Record() = default;
   Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
-      : Name(Name), Linkage(Linkage), Flags(Flags) {}
+      : Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {}
 
   bool isWeakDefined() const {
     return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined;
@@ -79,6 +80,10 @@ class Record {
   bool isRexported() const { return Linkage == RecordLinkage::Rexported; }
 
   StringRef getName() const { return Name; }
+  SymbolFlags getFlags() const { return Flags; }
+
+private:
+  SymbolFlags mergeFlags(SymbolFlags Flags, RecordLinkage Linkage);
 
 protected:
   StringRef Name;
@@ -137,6 +142,7 @@ class ObjCContainerRecord : public Record {
 
   ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage);
   ObjCIVarRecord *findObjCIVar(StringRef IVar) const;
+  std::vector<ObjCIVarRecord *> getObjCIVars() const;
 
 private:
   RecordMap<ObjCIVarRecord> IVars;
@@ -163,6 +169,7 @@ class ObjCInterfaceRecord : public ObjCContainerRecord {
 
   bool hasExceptionAttribute() const { return HasEHType; }
   bool addObjCCategory(ObjCCategoryRecord *Record);
+  std::vector<ObjCCategoryRecord *> getObjCCategories() const;
 
 private:
   bool HasEHType;
diff --git a/llvm/include/llvm/TextAPI/RecordVisitor.h b/llvm/include/llvm/TextAPI/RecordVisitor.h
new file mode 100644
index 00000000000000..34e43f5b0027f8
--- /dev/null
+++ b/llvm/include/llvm/TextAPI/RecordVisitor.h
@@ -0,0 +1,54 @@
+//===- llvm/TextAPI/RecordSlice.h - TAPI RecordSlice ------------*- 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Defines the TAPI Record Visitor.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TEXTAPI_RECORDVISITOR_H
+#define LLVM_TEXTAPI_RECORDVISITOR_H
+
+#include "llvm/TextAPI/Record.h"
+#include "llvm/TextAPI/SymbolSet.h"
+
+namespace llvm {
+namespace MachO {
+
+/// Base class for any usage of traversing over collected Records.
+class RecordVisitor {
+public:
+  virtual ~RecordVisitor();
+
+  virtual void visitGlobal(const GlobalRecord &) = 0;
+  virtual void visitObjCInterface(const ObjCInterfaceRecord &);
+  virtual void visitObjCCategory(const ObjCCategoryRecord &);
+};
+
+/// Specialized RecordVisitor for collecting exported symbols
+/// and undefined symbols if RecordSlice being visited represents a
+/// flat-namespaced library.
+class SymbolConverter : public RecordVisitor {
+public:
+  SymbolConverter(SymbolSet *Symbols, const Target &T,
+                  const bool RecordUndefs = false)
+      : Symbols(Symbols), Targ(T), RecordUndefs(RecordUndefs) {}
+  void visitGlobal(const GlobalRecord &) override;
+  void visitObjCInterface(const ObjCInterfaceRecord &) override;
+  void visitObjCCategory(const ObjCCategoryRecord &) override;
+
+private:
+  void addIVars(const ArrayRef<ObjCIVarRecord *>, StringRef ContainerName);
+  SymbolSet *Symbols;
+  const Target Targ;
+  const bool RecordUndefs;
+};
+
+} // end namespace MachO.
+} // end namespace llvm.
+
+#endif // LLVM_TEXTAPI_RECORDVISITOR_H
diff --git a/llvm/include/llvm/TextAPI/RecordsSlice.h b/llvm/include/llvm/TextAPI/RecordsSlice.h
index 461a6d2dcc5768..1f0962c15e84e8 100644
--- a/llvm/include/llvm/TextAPI/RecordsSlice.h
+++ b/llvm/include/llvm/TextAPI/RecordsSlice.h
@@ -14,11 +14,11 @@
 #ifndef LLVM_TEXTAPI_RECORDSLICE_H
 #define LLVM_TEXTAPI_RECORDSLICE_H
 
-#include "llvm/ADT/MapVector.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/TextAPI/InterfaceFile.h"
 #include "llvm/TextAPI/PackedVersion.h"
 #include "llvm/TextAPI/Record.h"
+#include "llvm/TextAPI/RecordVisitor.h"
 
 namespace llvm {
 namespace MachO {
@@ -133,6 +133,9 @@ class RecordsSlice {
            Categories.empty();
   }
 
+  // Visit all records known to RecordsSlice.
+  void visit(RecordVisitor &V) const;
+
   struct BinaryAttrs {
     std::vector<StringRef> AllowableClients;
     std::vector<StringRef> RexportedLibraries;
@@ -182,6 +185,7 @@ class RecordsSlice {
 };
 
 using Records = llvm::SmallVector<std::shared_ptr<RecordsSlice>, 4>;
+std::unique_ptr<InterfaceFile> convertToInterfaceFile(const Records &Slices);
 
 } // namespace MachO
 } // namespace llvm
diff --git a/llvm/lib/TextAPI/CMakeLists.txt b/llvm/lib/TextAPI/CMakeLists.txt
index 357e563064c78f..d825970d10494d 100644
--- a/llvm/lib/TextAPI/CMakeLists.txt
+++ b/llvm/lib/TextAPI/CMakeLists.txt
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMTextAPI
   PackedVersion.cpp
   Platform.cpp
   RecordsSlice.cpp
+  RecordVisitor.cpp
   Symbol.cpp
   SymbolSet.cpp
   Target.cpp
diff --git a/llvm/lib/TextAPI/DylibReader.cpp b/llvm/lib/TextAPI/DylibReader.cpp
index b6cfaaa75e8ae9..4df63ab08af6e0 100644
--- a/llvm/lib/TextAPI/DylibReader.cpp
+++ b/llvm/lib/TextAPI/DylibReader.cpp
@@ -409,3 +409,13 @@ Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
     return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
   return Results;
 }
+
+Expected<std::unique_ptr<InterfaceFile>>
+DylibReader::get(MemoryBufferRef Buffer) {
+  ParseOption Options;
+  auto SlicesOrErr = readFile(Buffer, Options);
+  if (!SlicesOrErr)
+    return SlicesOrErr.takeError();
+
+  return convertToInterfaceFile(*SlicesOrErr);
+}
diff --git a/llvm/lib/TextAPI/RecordVisitor.cpp b/llvm/lib/TextAPI/RecordVisitor.cpp
new file mode 100644
index 00000000000000..43f9b23dfefd7c
--- /dev/null
+++ b/llvm/lib/TextAPI/RecordVisitor.cpp
@@ -0,0 +1,67 @@
+//===- RecordVisitor.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Implements the TAPI Record Visitor.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TextAPI/RecordVisitor.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+RecordVisitor::~RecordVisitor() {}
+void RecordVisitor::visitObjCInterface(const ObjCInterfaceRecord &) {}
+void RecordVisitor::visitObjCCategory(const ObjCCategoryRecord &) {}
+
+namespace {
+bool shouldSkipRecord(const Record &R, const bool RecordUndefs) {
+  if (R.isExported())
+    return false;
+
+  // Skip non exported symbols unless for flat namespace libraries.
+  return !(RecordUndefs && R.isUndefined());
+}
+} // namespace
+
+void SymbolConverter::visitGlobal(const GlobalRecord &GR) {
+  auto [SymName, SymKind] = parseSymbol(GR.getName(), GR.getFlags());
+  if (shouldSkipRecord(GR, RecordUndefs))
+    return;
+  Symbols->addGlobal(SymKind, SymName, GR.getFlags(), Targ);
+}
+
+void SymbolConverter::addIVars(const ArrayRef<ObjCIVarRecord *> IVars,
+                               StringRef ContainerName) {
+  for (auto *IV : IVars) {
+    if (shouldSkipRecord(*IV, RecordUndefs))
+      continue;
+    std::string Name =
+        ObjCIVarRecord::createScopedName(ContainerName, IV->getName());
+    Symbols->addGlobal(SymbolKind::ObjectiveCInstanceVariable, Name,
+                       IV->getFlags(), Targ);
+  }
+}
+
+void SymbolConverter::visitObjCInterface(const ObjCInterfaceRecord &ObjCR) {
+  if (!shouldSkipRecord(ObjCR, RecordUndefs)) {
+    Symbols->addGlobal(SymbolKind::ObjectiveCClass, ObjCR.getName(),
+                       ObjCR.getFlags(), Targ);
+    if (ObjCR.hasExceptionAttribute())
+      Symbols->addGlobal(SymbolKind::ObjectiveCClassEHType, ObjCR.getName(),
+                         ObjCR.getFlags(), Targ);
+  }
+
+  addIVars(ObjCR.getObjCIVars(), ObjCR.getName());
+  for (const auto *Cat : ObjCR.getObjCCategories())
+    addIVars(Cat->getObjCIVars(), ObjCR.getName());
+}
+
+void SymbolConverter::visitObjCCategory(const ObjCCategoryRecord &Cat) {
+  addIVars(Cat.getObjCIVars(), Cat.getName());
+}
diff --git a/llvm/lib/TextAPI/RecordsSlice.cpp b/llvm/lib/TextAPI/RecordsSlice.cpp
index a220b255aea381..3b456d77cbcc7c 100644
--- a/llvm/lib/TextAPI/RecordsSlice.cpp
+++ b/llvm/lib/TextAPI/RecordsSlice.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/TextAPI/RecordsSlice.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/TextAPI/Record.h"
 #include "llvm/TextAPI/Symbol.h"
 #include <utility>
@@ -142,8 +143,10 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
   if (Result.second)
     Result.first->second =
         std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV);
-  else
+  else {
     updateLinkage(Result.first->second.get(), Linkage);
+    Result.first->second.get()->Flags = Flags;
+  }
   return Result.first->second.get();
 }
 
@@ -164,6 +167,19 @@ ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name,
 
   return Result.first->second.get();
 }
+SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) {
+  // Add Linkage properties into Flags.
+  switch (Linkage) {
+  case RecordLinkage::Rexported:
+    Flags |= SymbolFlags::Rexported;
+    return Flags;
+  case RecordLinkage::Undefined:
+    Flags |= SymbolFlags::Undefined;
+    return Flags;
+  default:
+    return Flags;
+  }
+}
 
 bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) {
   auto Result = Categories.insert({Name, Record});
@@ -188,11 +204,26 @@ ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend,
   return Result.first->second.get();
 }
 
+std::vector<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const {
+  std::vector<ObjCIVarRecord *> Records;
+  llvm::for_each(IVars,
+                 [&](auto &Record) { Records.push_back(Record.second.get()); });
+  return Records;
+}
+
+std::vector<ObjCCategoryRecord *>
+ObjCInterfaceRecord::getObjCCategories() const {
+  std::vector<ObjCCategoryRecord *> Records;
+  llvm::for_each(Categories,
+                 [&](auto &Record) { Records.push_back(Record.second); });
+  return Records;
+}
+
 ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar,
                                                  RecordLinkage Linkage) {
   auto Result = IVars.insert({IVar, nullptr});
   if (Result.second)
-    Result.first->second = std::make_unique<ObjCIVarRecord>(Name, Linkage);
+    Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage);
   return Result.first->second.get();
 }
 
@@ -222,3 +253,92 @@ RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() {
     BA = std::make_unique<BinaryAttrs>();
   return *BA;
 }
+
+void RecordsSlice::visit(RecordVisitor &V) const {
+  for (auto &G : Globals)
+    V.visitGlobal(*G.second);
+  for (auto &C : Classes)
+    V.visitObjCInterface(*C.second);
+  for (auto &Cat : Categories)
+    V.visitObjCCategory(*Cat.second);
+}
+
+namespace {
+
+std::unique_ptr<InterfaceFile> createInterfaceFile(const Records &Slices,
+                                                   StringRef InstallName) {
+  // Pickup symbols first.
+  auto Symbols = std::make_unique<SymbolSet>();
+  for (auto &S : Slices) {
+    if (S->empty())
+      continue;
+    auto &BA = S->getBinaryAttrs();
+    if (BA.InstallName != InstallName)
+      continue;
+
+    SymbolConverter Converter(Symbols.get(), S->getTarget(),
+                              !BA.TwoLevelNamespace);
+    S->visit(Converter);
+  }
+
+  auto File = std::make_unique<InterfaceFile>(std::move(Symbols));
+  File->setInstallName(InstallName);
+  // Assign other attributes.
+  for (auto &S : Slices) {
+    if (S->empty())
+      continue;
+    auto &BA = S->getBinaryAttrs();
+    if (BA.InstallName != InstallName)
+      continue;
+    const Target &Targ = S->getTarget();
+    File->addTarget(Targ);
+    if (File->getFileType() == FileType::Invalid)
+      File->setFileType(BA.File);
+    if (BA.AppExtensionSafe && !File->isApplicationExtensionSafe())
+      File->setApplicationExtensionSafe();
+    if (BA.TwoLevelNamespace && !File->isTwoLevelNamespace())
+      File->setTwoLevelNamespace();
+    if (BA.OSLibNotForSharedCache && !File->isOSLibNotForSharedCache())
+      File->setOSLibNotForSharedCache();
+    if (File->getCurrentVersion().empty())
+      File->setCurrentVersion(BA.CurrentVersion);
+    if (File->getCompatibilityVersion().empty())
+      File->setCompatibilityVersion(BA.CompatVersion);
+    if (File->getSwiftABIVersion() == 0)
+      File->setSwiftABIVersion(BA.SwiftABI);
+    if (File->getPath().empty())
+      File->setPath(BA.Path);
+    if (!BA.ParentUmbrella.empty())
+      File->addParentUmbrella(Targ, BA.ParentUmbrella);
+    for (const auto &Client : BA.AllowableClients)
+      File->addAllowableClient(Client, Targ);
+    for (const auto &Lib : BA.RexportedLibraries)
+      File->addReexportedLibrary(Lib, Targ);
+  }
+
+  return File;
+}
+
+} // namespace
+
+std::unique_ptr<InterfaceFile>
+llvm::MachO::convertToInterfaceFile(const Records &Slices) {
+  std::unique_ptr<InterfaceFile> File;
+  if (Slices.empty())
+    return File;
+
+  SetVector<StringRef> InstallNames;
+  for (auto &S : Slices) {
+    auto Name = S->getBinaryAttrs().InstallName;
+    if (Name.empty())
+      continue;
+    InstallNames.insert(Name);
+  }
+
+  File = createInterfaceFile(Slices, *InstallNames.begin());
+  for (auto it = std::next(InstallNames.begin()); it != InstallNames.end();
+       ++it)
+    File->addDocument(createInterfaceFile(Slices, *it));
+
+  return File;
+}

>From d110ce351c32decaa44de9bef3565c735ec44cfb Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 11 Dec 2023 09:29:34 -0800
Subject: [PATCH 2/2] swap anon namespacing with `static`

---
 llvm/lib/TextAPI/RecordVisitor.cpp | 4 +---
 llvm/lib/TextAPI/RecordsSlice.cpp  | 8 ++------
 2 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/TextAPI/RecordVisitor.cpp b/llvm/lib/TextAPI/RecordVisitor.cpp
index 43f9b23dfefd7c..cee04e6447555b 100644
--- a/llvm/lib/TextAPI/RecordVisitor.cpp
+++ b/llvm/lib/TextAPI/RecordVisitor.cpp
@@ -19,15 +19,13 @@ RecordVisitor::~RecordVisitor() {}
 void RecordVisitor::visitObjCInterface(const ObjCInterfaceRecord &) {}
 void RecordVisitor::visitObjCCategory(const ObjCCategoryRecord &) {}
 
-namespace {
-bool shouldSkipRecord(const Record &R, const bool RecordUndefs) {
+static bool shouldSkipRecord(const Record &R, const bool RecordUndefs) {
   if (R.isExported())
     return false;
 
   // Skip non exported symbols unless for flat namespace libraries.
   return !(RecordUndefs && R.isUndefined());
 }
-} // namespace
 
 void SymbolConverter::visitGlobal(const GlobalRecord &GR) {
   auto [SymName, SymKind] = parseSymbol(GR.getName(), GR.getFlags());
diff --git a/llvm/lib/TextAPI/RecordsSlice.cpp b/llvm/lib/TextAPI/RecordsSlice.cpp
index 3b456d77cbcc7c..b6b08d485d706c 100644
--- a/llvm/lib/TextAPI/RecordsSlice.cpp
+++ b/llvm/lib/TextAPI/RecordsSlice.cpp
@@ -263,10 +263,8 @@ void RecordsSlice::visit(RecordVisitor &V) const {
     V.visitObjCCategory(*Cat.second);
 }
 
-namespace {
-
-std::unique_ptr<InterfaceFile> createInterfaceFile(const Records &Slices,
-                                                   StringRef InstallName) {
+static std::unique_ptr<InterfaceFile>
+createInterfaceFile(const Records &Slices, StringRef InstallName) {
   // Pickup symbols first.
   auto Symbols = std::make_unique<SymbolSet>();
   for (auto &S : Slices) {
@@ -319,8 +317,6 @@ std::unique_ptr<InterfaceFile> createInterfaceFile(const Records &Slices,
   return File;
 }
 
-} // namespace
-
 std::unique_ptr<InterfaceFile>
 llvm::MachO::convertToInterfaceFile(const Records &Slices) {
   std::unique_ptr<InterfaceFile> File;



More information about the llvm-branch-commits mailing list