Skip to content

TBDGen: some refactoring to allow multiple platform kinds for linker directives. NFC #29241

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 1 commit into from
Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 9 additions & 2 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1623,8 +1623,15 @@ class OriginallyDefinedInAttr: public DeclAttribute {
/// Indicates when the symbol was moved here.
const llvm::VersionTuple MovedVersion;

/// Returns true if this attribute is active given the current platform.
bool isActivePlatform(const ASTContext &ctx) const;
struct ActiveVersion {
PlatformKind Platform;
bool IsSimulator;
llvm::VersionTuple Version;
};

/// Returns non-optional if this attribute is active given the current platform.
/// The value provides more details about the active platform.
Optional<ActiveVersion> isActivePlatform(const ASTContext &ctx) const;
static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_OriginallyDefinedIn;
}
Expand Down
12 changes: 10 additions & 2 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1293,8 +1293,16 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const {
return isPlatformActive(Platform, ctx.LangOpts);
}

bool OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const {
return isPlatformActive(Platform, ctx.LangOpts);
Optional<OriginallyDefinedInAttr::ActiveVersion>
OriginallyDefinedInAttr::isActivePlatform(const ASTContext &ctx) const {
OriginallyDefinedInAttr::ActiveVersion Result;
Result.Platform = Platform;
Result.Version = MovedVersion;
if (isPlatformActive(Platform, ctx.LangOpts)) {
Result.IsSimulator = ctx.LangOpts.Target.isSimulatorEnvironment();
return Result;
}
return None;
}

bool AvailableAttr::isLanguageVersionSpecific() const {
Expand Down
132 changes: 74 additions & 58 deletions lib/TBDGen/TBDGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,31 @@ void TBDGenVisitor::addSymbolInternal(StringRef name,
}
}

static Optional<llvm::VersionTuple> getDeclMoveOSVersion(Decl *D) {
static std::vector<OriginallyDefinedInAttr::ActiveVersion>
getAllMovedPlatformVersions(Decl *D) {
std::vector<OriginallyDefinedInAttr::ActiveVersion> Results;
for (auto *attr: D->getAttrs()) {
if (auto *ODA = dyn_cast<OriginallyDefinedInAttr>(attr)) {
if (ODA->isActivePlatform(D->getASTContext()))
return ODA->MovedVersion;
auto Active = ODA->isActivePlatform(D->getASTContext());
if (Active.hasValue()) {
Results.push_back(*Active);
}
}
}
return None;
return Results;
}

enum class LinkerPlatformId: uint8_t {
#define LD_PLATFORM(Name, Id) Name = Id,
#include "ldPlatformKinds.def"
};
static Optional<llvm::VersionTuple>
getIntroducedOSVersion(Decl *D, PlatformKind Kind) {
for (auto *attr: D->getAttrs()) {
if (auto *ava = dyn_cast<AvailableAttr>(attr)) {
if (ava->Platform == Kind && ava->Introduced) {
return ava->Introduced;
}
}
}
return None;
}

static StringRef getLinkerPlatformName(uint8_t Id) {
switch (Id) {
Expand All @@ -103,29 +114,23 @@ static Optional<uint8_t> getLinkerPlatformId(StringRef Platform) {
.Default(None);
}

struct InstallNameStore {
// The default install name to use when no specific install name is specified.
std::string InstallName;
// The install name specific to the platform id. This takes precedence over
// the default install name.
std::map<uint8_t, std::string> PlatformInstallName;
StringRef getInstallName(LinkerPlatformId Id) const {
auto It = PlatformInstallName.find((uint8_t)Id);
if (It == PlatformInstallName.end())
return InstallName;
else
return It->second;
}
void remark(ASTContext &Ctx, StringRef ModuleName) const {
Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name,
ModuleName, InstallName);
for (auto Pair: PlatformInstallName) {
Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name,
ModuleName, getLinkerPlatformName(Pair.first),
Pair.second);
}
StringRef InstallNameStore::getInstallName(LinkerPlatformId Id) const {
auto It = PlatformInstallName.find((uint8_t)Id);
if (It == PlatformInstallName.end())
return InstallName;
else
return It->second;
}

void InstallNameStore::remark(ASTContext &Ctx, StringRef ModuleName) const {
Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name,
ModuleName, InstallName);
for (auto Pair: PlatformInstallName) {
Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name,
ModuleName, getLinkerPlatformName(Pair.first),
Pair.second);
}
};
}

static std::string getScalaNodeText(Node *N) {
SmallString<32> Buffer;
Expand Down Expand Up @@ -195,37 +200,49 @@ parseEntry(ASTContext &Ctx,
return 0;
}

static std::map<std::string, InstallNameStore>
parsePreviousModuleInstallNameMap(ASTContext &Ctx, StringRef FileName) {
std::unique_ptr<std::map<std::string, InstallNameStore>>
TBDGenVisitor::parsePreviousModuleInstallNameMap() {
StringRef FileName = Opts.ModuleInstallNameMapPath;
// Nothing to parse.
if (FileName.empty())
return nullptr;
namespace yaml = llvm::yaml;
std::map<std::string, InstallNameStore> AllInstallNames;
ASTContext &Ctx = SwiftModule->getASTContext();
std::unique_ptr<std::map<std::string, InstallNameStore>> pResult(
new std::map<std::string, InstallNameStore>());
auto &AllInstallNames = *pResult;
SWIFT_DEFER {
for (auto Pair: AllInstallNames) {
Pair.second.remark(Ctx, Pair.first);
}
};

// Load the input file.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
vfs::getFileOrSTDIN(*Ctx.SourceMgr.getFileSystem(), FileName);
llvm::MemoryBuffer::getFile(FileName);
if (!FileBufOrErr) {
Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_missing,
FileName);
return AllInstallNames;
return nullptr;
}
StringRef Buffer = FileBufOrErr->get()->getBuffer();

// Use a new source manager instead of the one from ASTContext because we
// don't want the Json file to be persistent.
SourceManager SM;
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, FileName),
Ctx.SourceMgr.getLLVMSourceMgr());
SM.getLLVMSourceMgr());
for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) {
assert(DI != Stream.end() && "Failed to read a document");
yaml::Node *N = DI->getRoot();
assert(N && "Failed to find a root");
if (parseEntry(Ctx, N, AllInstallNames)) {
Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_corrupted,
FileName);
return AllInstallNames;
return nullptr;
}
}
return AllInstallNames;
return pResult;
}

void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name,
Expand All @@ -234,26 +251,29 @@ void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name,
return;
if (!TopLevelDecl)
return;
auto MovedVer = getDeclMoveOSVersion(TopLevelDecl);
if (!MovedVer.hasValue())
auto MovedVers = getAllMovedPlatformVersions(TopLevelDecl);
if (MovedVers.empty())
return;
assert(MovedVer.hasValue());
assert(!MovedVers.empty());

// Using $ld$add and $ld$hide cannot encode platform name in the version number,
// so we can only handle one version.
// FIXME: use $ld$previous instead
auto MovedVer = MovedVers.front().Version;
auto Platform = MovedVers.front().Platform;
unsigned Major[2];
unsigned Minor[2];
Major[1] = MovedVer->getMajor();
Minor[1] = MovedVer->getMinor().hasValue() ? *MovedVer->getMinor(): 0;
auto AvailRange = AvailabilityInference::availableRange(TopLevelDecl,
TopLevelDecl->getASTContext()).getOSVersion();
assert(AvailRange.hasLowerEndpoint() &&
"cannot find the start point of availability");
if (!AvailRange.hasLowerEndpoint())
Major[1] = MovedVer.getMajor();
Minor[1] = MovedVer.getMinor().hasValue() ? *MovedVer.getMinor(): 0;
auto IntroVer = getIntroducedOSVersion(TopLevelDecl, Platform);
assert(IntroVer && "cannot find the start point of availability");
if (!IntroVer.hasValue())
return;
assert(AvailRange.getLowerEndpoint() < *MovedVer);
if (AvailRange.getLowerEndpoint() >= *MovedVer)
assert(*IntroVer < MovedVer);
if (*IntroVer >= MovedVer)
return;
Major[0] = AvailRange.getLowerEndpoint().getMajor();
Minor[0] = AvailRange.getLowerEndpoint().getMinor().hasValue() ?
AvailRange.getLowerEndpoint().getMinor().getValue() : 0;
Major[0] = IntroVer->getMajor();
Minor[0] = IntroVer->getMinor().hasValue() ? IntroVer->getMinor().getValue() : 0;
for (auto CurMaj = Major[0]; CurMaj <= Major[1]; ++ CurMaj) {
unsigned MinRange[2] = {0, 31};
if (CurMaj == Major[0])
Expand Down Expand Up @@ -850,7 +870,7 @@ static bool isApplicationExtensionSafe(const LangOptions &LangOpts) {
}

static bool hasLinkerDirective(Decl *D) {
return getDeclMoveOSVersion(D).hasValue();
return !getAllMovedPlatformVersions(D).empty();
}

static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile,
Expand Down Expand Up @@ -882,10 +902,6 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile,
file.setCompatibilityVersion(*packed);
}

if (!opts.ModuleInstallNameMapPath.empty()) {
parsePreviousModuleInstallNameMap(ctx, opts.ModuleInstallNameMapPath);
}

llvm::MachO::Target target(triple);
file.addTarget(target);

Expand Down
22 changes: 21 additions & 1 deletion lib/TBDGen/TBDGenVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ struct TBDGenOptions;

namespace tbdgen {

enum class LinkerPlatformId: uint8_t {
#define LD_PLATFORM(Name, Id) Name = Id,
#include "ldPlatformKinds.def"
};

struct InstallNameStore {
// The default install name to use when no specific install name is specified.
std::string InstallName;
// The install name specific to the platform id. This takes precedence over
// the default install name.
std::map<uint8_t, std::string> PlatformInstallName;
StringRef getInstallName(LinkerPlatformId Id) const;
void remark(ASTContext &Ctx, StringRef ModuleName) const;
};

class TBDGenVisitor : public ASTVisitor<TBDGenVisitor> {
public:
llvm::MachO::InterfaceFile &Symbols;
Expand All @@ -57,6 +72,10 @@ class TBDGenVisitor : public ASTVisitor<TBDGenVisitor> {
Decl* TopLevelDecl = nullptr;

private:
std::unique_ptr<std::map<std::string, InstallNameStore>>
previousInstallNameMap;
std::unique_ptr<std::map<std::string, InstallNameStore>>
parsePreviousModuleInstallNameMap();
void addSymbolInternal(StringRef name, llvm::MachO::SymbolKind kind,
bool isLinkerDirective = false);
void addLinkerDirectiveSymbols(StringRef name, llvm::MachO::SymbolKind kind);
Expand Down Expand Up @@ -86,7 +105,8 @@ class TBDGenVisitor : public ASTVisitor<TBDGenVisitor> {
ModuleDecl *swiftModule, const TBDGenOptions &opts)
: Symbols(symbols), Targets(targets), StringSymbols(stringSymbols),
DataLayout(dataLayout), UniversalLinkInfo(universalLinkInfo),
SwiftModule(swiftModule), Opts(opts) {}
SwiftModule(swiftModule), Opts(opts),
previousInstallNameMap(parsePreviousModuleInstallNameMap()) {}

void addMainIfNecessary(FileUnit *file) {
// HACK: 'main' is a special symbol that's always emitted in SILGen if
Expand Down
2 changes: 0 additions & 2 deletions test/TBD/previous-install-name-map.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@
// CHECK: remark: previous install name for Foo in iOS is /System/Others
// CHECK: remark: previous install name for Foo in tvOS is /System/Others
// CHECK: remark: previous install name for Foo in watchOS is /System/Others

// REQUIRES: rdar58569201