Skip to content

[TextAPI] Introduce Records & RecordSlice Types #74115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions llvm/include/llvm/TextAPI/Record.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//===- llvm/TextAPI/Record.h - TAPI Record ----------------------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Implements the TAPI Record Types.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_TEXTAPI_RECORD_H
#define LLVM_TEXTAPI_RECORD_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/TextAPI/Symbol.h"
#include <string>

namespace llvm {
namespace MachO {

LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();

class RecordsSlice;

// Defines a list of linkage types.
enum class RecordLinkage : uint8_t {
// Unknown linkage.
Unknown = 0,

// Local, hidden or private extern linkage.
Internal = 1,

// Undefined linkage, it represents usage of external interface.
Undefined = 2,

// Re-exported linkage, record is defined in external interface.
Rexported = 3,

// Exported linkage.
Exported = 4,
};

/// Define Record. They represent API's in binaries that could be linkable
/// symbols.
class Record {
public:
Record() = default;
Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
: Name(Name), Linkage(Linkage), Flags(Flags) {}

bool isWeakDefined() const {
return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined;
}

bool isWeakReferenced() const {
return (Flags & SymbolFlags::WeakReferenced) == SymbolFlags::WeakReferenced;
}

bool isThreadLocalValue() const {
return (Flags & SymbolFlags::ThreadLocalValue) ==
SymbolFlags::ThreadLocalValue;
}

bool isData() const {
return (Flags & SymbolFlags::Data) == SymbolFlags::Data;
}

bool isText() const {
return (Flags & SymbolFlags::Text) == SymbolFlags::Text;
}

bool isInternal() const { return Linkage == RecordLinkage::Internal; }
bool isUndefined() const { return Linkage == RecordLinkage::Undefined; }
bool isExported() const { return Linkage >= RecordLinkage::Rexported; }
bool isRexported() const { return Linkage == RecordLinkage::Rexported; }

StringRef getName() const { return Name; }

protected:
StringRef Name;
RecordLinkage Linkage;
SymbolFlags Flags;

friend class RecordsSlice;
};

// Defines broadly non-objc records, categorized as variables or functions.
class GlobalRecord : public Record {
public:
enum class Kind : uint8_t {
Unknown = 0,
Variable = 1,
Function = 2,
};

GlobalRecord(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags,
Kind GV)
: Record({Name, Linkage, Flags}), GV(GV) {}

bool isFunction() const { return GV == Kind::Function; }
bool isVariable() const { return GV == Kind::Variable; }

private:
Kind GV;
};

// Define Objective-C instance variable records.
struct ObjCIVarRecord : public Record {
public:
ObjCIVarRecord(StringRef Name, RecordLinkage Linkage)
: Record({Name, Linkage, SymbolFlags::Data}) {}

static std::string createScopedName(StringRef SuperClass, StringRef IVar) {
return (SuperClass + "." + IVar).str();
}
};

template <typename V, typename K = StringRef,
typename std::enable_if<std::is_base_of<Record, V>::value>::type * =
nullptr>
using RecordMap = llvm::MapVector<K, std::unique_ptr<V>>;

// Defines Objective-C record types that have assigned methods, properties,
// instance variable (ivars) and protocols.
class ObjCContainerRecord : public Record {
public:
ObjCContainerRecord(StringRef Name, RecordLinkage Linkage)
: Record({Name, Linkage, SymbolFlags::Data}) {}

ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage);
ObjCIVarRecord *findObjCIVar(StringRef IVar) const;

private:
RecordMap<ObjCIVarRecord> IVars;
};

// Define Objective-C category types. They don't generate linkable symbols, but
// they have assigned ivars that do.
class ObjCCategoryRecord : public ObjCContainerRecord {
public:
ObjCCategoryRecord(StringRef ClassToExtend, StringRef Name)
: ObjCContainerRecord(Name, RecordLinkage::Unknown),
ClassToExtend(ClassToExtend) {}

private:
StringRef ClassToExtend;
};

// Define Objective-C Interfaces or class types.
class ObjCInterfaceRecord : public ObjCContainerRecord {
public:
ObjCInterfaceRecord(StringRef Name, RecordLinkage Linkage,
bool HasEHType = false)
: ObjCContainerRecord(Name, Linkage), HasEHType(HasEHType) {}

bool hasExceptionAttribute() const { return HasEHType; }
bool addObjCCategory(ObjCCategoryRecord *Record);

private:
bool HasEHType;
// Non-owning containers of categories that extend the class.
llvm::MapVector<StringRef, ObjCCategoryRecord *> Categories;
};

} // end namespace MachO.
} // end namespace llvm.

#endif // LLVM_TEXTAPI_RECORD_H
183 changes: 183 additions & 0 deletions llvm/include/llvm/TextAPI/RecordsSlice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//===- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Implements the TAPI Record Collection Type.
///
//===----------------------------------------------------------------------===//

#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"

namespace llvm {
namespace MachO {

// Define collection of records for a library that are tied to a darwin target
// triple.
class RecordsSlice {
public:
RecordsSlice(const llvm::Triple &T) : TargetTriple(T), Target(T) {}
/// Get target triple.
const llvm::Triple &getTriple() const { return TargetTriple; }
/// Get TAPI converted target.
const Target &getTarget() const { return Target; }

/// Add unspecified record to slice.
///
/// Assign specific record type based on properties and symbol name.
///
/// \param Name The name of symbol.
/// \param Flags The flags that describe attributes of the symbol.
/// \param GV The kind of global, if this represents a non obj-c global
/// symbol.
/// \param Linkage The linkage of symbol.
/// \return The non-owning pointer to added record in slice.
Record *addRecord(StringRef Name, SymbolFlags Flags,
GlobalRecord::Kind GV = GlobalRecord::Kind::Unknown,
RecordLinkage Linkage = RecordLinkage::Unknown);

/// Add non-ObjC global record.
///
/// \param Name The name of symbol.
/// \param Flags The flags that describe attributes of the symbol.
/// \param GV The kind of global.
/// \param Linkage The linkage of symbol.
/// \return The non-owning pointer to added record in slice.
GlobalRecord *addGlobal(StringRef Name, RecordLinkage Linkage,
GlobalRecord::Kind GV,
SymbolFlags Flags = SymbolFlags::None);

/// Add ObjC Class record.
///
/// \param Name The name of class, not symbol.
/// \param Linkage The linkage of symbol.
/// \param HasEHType Whether symbol represents an eh_type.
/// \return The non-owning pointer to added record in slice.
ObjCInterfaceRecord *addObjCInterface(StringRef Name, RecordLinkage Linkage,
bool HasEHType = false);

/// Add ObjC IVar record.
///
/// \param Name The name of ivar, not symbol.
/// \param Linkage The linkage of symbol.
/// \return The non-owning pointer to added record in slice.
ObjCIVarRecord *addObjCIVar(ObjCContainerRecord *Container, StringRef Name,
RecordLinkage Linkage);

/// Add ObjC Category record.
///
/// \param ClassToExtend The name of class that is being extended by the
/// category, not symbol.
/// \param Category The name of category.
/// \return The non-owning pointer to added record in slice.
ObjCCategoryRecord *addObjCCategory(StringRef ClassToExtend,
StringRef Category);

/// Find ObjC Class.
///
/// \param Name name of class, not full symbol name.
/// \return The non-owning pointer to record in slice.
ObjCInterfaceRecord *findObjCInterface(StringRef Name) const;

/// Find ObjC Category.
///
/// \param ClassToExtend The name of class, not full symbol name.
/// \param Categories The name of category.
/// \return The non-owning pointer to record in slice.
ObjCCategoryRecord *findObjCCategory(StringRef ClassToExtend,
StringRef Category) const;

/// Find ObjC Container. This is commonly used for assigning for looking up
/// instance variables that are assigned to either a category or class.
///
/// \param IsIVar If true, the name is the name of the IVar, otherwise it will
/// be looked up as the name of the container.
/// \param Name Either the name of ivar or name of container.
/// \return The non-owning pointer to record in
/// slice.
ObjCContainerRecord *findContainer(bool IsIVar, StringRef Name) const;

/// Find ObjC instance variable.
///
/// \param IsScopedName This is used to determine how to parse the name.
/// \param Name Either the full name of the symbol or just the ivar.
/// \return The non-owning pointer to record in slice.
ObjCIVarRecord *findObjCIVar(bool IsScopedName, StringRef Name) const;

/// Find non-objc global.
///
/// \param Name The name of symbol.
/// \param GV The Kind of global to find.
/// \return The non-owning pointer to record in slice.
GlobalRecord *
findGlobal(StringRef Name,
GlobalRecord::Kind GV = GlobalRecord::Kind::Unknown) const;

// Determine if library attributes were assigned.
bool hasBinaryAttrs() const { return BA.get(); }

// Determine if record slice is unassigned.
bool isEmpty() const {
return !hasBinaryAttrs() && Globals.empty() && Classes.empty() &&
Categories.empty();
}

struct BinaryAttrs {
std::vector<StringRef> AllowableClients;
std::vector<StringRef> RexportedLibraries;
std::vector<StringRef> RPaths;
StringRef ParentUmbrella;
StringRef InstallName;
StringRef UUID;
StringRef Path;
FileType fileType = FileType::Invalid;
llvm::MachO::PackedVersion CurrentVersion;
llvm::MachO::PackedVersion CompatVersion;
uint8_t SwiftABI = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is SwiftABI still used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, the linker continues to check it. I imagine since abi stability the value is always 7 though. We chatted about this a while back with @cooperp, but the details are slipping my mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, the linker still enforces that the Swift ABI matches in all object files. Thats logic we needed back when changing the ABI happened each release. Its still there, but just never finds a mismatch.

If the Swift team did ever need to bump this version then the linker will be there to complain if it goes wrong :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the check/requirement just object files or also dylibs that link into the final linked image?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like dylibs too yeah. ld64 gets the swift version from used dylibs, tbds and object files, and makes sure they all match

bool TwoLevelNamespace = false;
bool AppExtensionSafe = false;
bool OSLibNotForSharedCache = false;
};

/// Return reference to BinaryAttrs.
BinaryAttrs &getBinaryAttrs();

private:
const llvm::Triple TargetTriple;
// Hold tapi converted triple to avoid unecessary casts.
const Target Target;

/// BumpPtrAllocator to store generated/copied strings.
llvm::BumpPtrAllocator StringAllocator;
StringRef copyString(StringRef String);

/// Promote linkage of requested record. It is no-op if linkage type is lower
/// than the current assignment.
///
/// \param R The record to update.
/// \param L Linkage type to update to.
void updateLinkage(Record *R, RecordLinkage L) {
R->Linkage = std::max(R->Linkage, L);
}

RecordMap<GlobalRecord> Globals;
RecordMap<ObjCInterfaceRecord> Classes;
RecordMap<ObjCCategoryRecord, std::pair<StringRef, StringRef>> Categories;

std::unique_ptr<BinaryAttrs> BA{nullptr};
};

} // namespace MachO
} // namespace llvm
#endif // LLVM_TEXTAPI_RECORDSLICE_H
17 changes: 17 additions & 0 deletions llvm/include/llvm/TextAPI/Symbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ class Symbol {
SymbolFlags Flags;
};

/// Lightweight struct for passing around symbol information.
struct SimpleSymbol {
StringRef Name;
SymbolKind Kind;

bool operator<(const SimpleSymbol &O) const {
return std::tie(Name, Kind) < std::tie(O.Name, O.Kind);
}
};

/// Determine SymbolKind from Flags and parsing Name.
///
/// \param Name The name of symbol.
/// \param Flags The flags pre-determined for the symbol.
SimpleSymbol parseSymbol(StringRef SymName,
const SymbolFlags Flags = SymbolFlags::None);

} // end namespace MachO.
} // end namespace llvm.

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/TextAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_llvm_component_library(LLVMTextAPI
TextStubV5.cpp
PackedVersion.cpp
Platform.cpp
RecordsSlice.cpp
Symbol.cpp
SymbolSet.cpp
Target.cpp
Expand Down
Loading