Skip to content

Name platform-specific module files using a normalized target triple #22842

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 6 commits into from
Mar 1, 2019
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
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,8 @@ ERROR(cannot_return_value_from_void_func,none,

ERROR(sema_no_import,Fatal,
"no such module '%0'", (StringRef))
ERROR(sema_no_import_arch,Fatal,
"could not find module '%0' for architecture '%1'; "
ERROR(sema_no_import_target,Fatal,
"could not find module '%0' for target '%1'; "
"found: %2", (StringRef, StringRef, StringRef))
ERROR(sema_no_import_repl,none,
"no such module '%0'", (StringRef))
Expand Down
11 changes: 11 additions & 0 deletions include/swift/Basic/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ namespace swift {
///
/// This is a stop-gap until full Triple support (ala Clang) exists within swiftc.
StringRef getMajorArchitectureName(const llvm::Triple &triple);

/// Computes the normalized target triple used as the most preferred name for
/// module loading.
///
/// For platforms with fat binaries, this canonicalizes architecture,
/// vendor, and OS names, strips OS versions, and makes inferred environments
/// explicit. For other platforms, it returns the unmodified triple.
///
/// The input triple should already be "normalized" in the sense that
/// llvm::Triple::normalize() would not affect it.
llvm::Triple getTargetSpecificModuleTriple(const llvm::Triple &triple);
} // end namespace swift

#endif // SWIFT_BASIC_PLATFORM_H
Expand Down
16 changes: 8 additions & 8 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ class SerializedModuleLoaderBase : public ModuleLoader {
/// to list the architectures that \e are present.
///
/// \returns true if an error diagnostic was emitted
virtual bool maybeDiagnoseArchitectureMismatch(SourceLoc sourceLocation,
StringRef moduleName,
StringRef archName,
StringRef directoryPath) {
virtual bool maybeDiagnoseTargetMismatch(SourceLoc sourceLocation,
StringRef moduleName,
StringRef archName,
StringRef directoryPath) {
return false;
}

Expand Down Expand Up @@ -139,10 +139,10 @@ class SerializedModuleLoader : public SerializedModuleLoaderBase {
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer) override;

bool maybeDiagnoseArchitectureMismatch(SourceLoc sourceLocation,
StringRef moduleName,
StringRef archName,
StringRef directoryPath) override;
bool maybeDiagnoseTargetMismatch(SourceLoc sourceLocation,
StringRef moduleName,
StringRef archName,
StringRef directoryPath) override;

public:
virtual ~SerializedModuleLoader();
Expand Down
125 changes: 125 additions & 0 deletions lib/Basic/Platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//

#include "swift/Basic/Platform.h"
#include "llvm/ADT/StringExtras.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: alphabet

#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"

using namespace swift;
Expand Down Expand Up @@ -188,3 +190,126 @@ StringRef swift::getMajorArchitectureName(const llvm::Triple &Triple) {
return Triple.getArchName();
}
}

// The code below is responsible for normalizing target triples into the form
// used to name target-specific swiftmodule, swiftinterface, and swiftdoc files.
// If two triples have incompatible ABIs or can be distinguished by Swift #if
// declarations, they should normalize to different values.
//
// This code is only really used on platforms with toolchains supporting fat
// binaries (a single binary containing multiple architectures). On these
// platforms, this code should strip unnecessary details from target triple
// components and map synonyms to canonical values. Even values which don't need
// any special canonicalization should be documented here as comments.
//
// (Fallback behavior does not belong here; it should be implemented in code
// that calls this function, most importantly in SerializedModuleLoaderBase.)
//
// If you're trying to refer to this code to understand how Swift behaves and
// you're unfamiliar with LLVM internals, here's a cheat sheet for reading it:
//
// * llvm::Triple is the type for a target name. It's a bit of a misnomer,
// because it can contain three or four values: arch-vendor-os[-environment].
//
// * In .Cases and .Case, the last argument is the value the arguments before it
// map to. That is, `.Cases("bar", "baz", "foo")` will return "foo" if it sees
// "bar" or "baz".
//
// * llvm::Optional is similar to a Swift Optional: it either contains a value
// or represents the absence of one. `None` is equivalent to `nil`; leading
// `*` is equivalent to trailing `!`; conversion to `bool` is a not-`None`
// check.

static StringRef
getArchForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) {
auto tripleArchName = triple.getArchName();

return llvm::StringSwitch<StringRef>(tripleArchName)
.Cases("arm64", "aarch64", "arm64")
.Cases("x86_64", "amd64", "x86_64")
.Cases("i386", "i486", "i586", "i686", "i786", "i886", "i986",
"i386")
.Cases("unknown", "", "unknown")
// These values are also supported, but are handled by the default case below:
// .Case ("armv7s", "armv7s")
// .Case ("armv7k", "armv7k")
// .Case ("armv7", "armv7")
.Default(tripleArchName);
Copy link
Contributor Author

@beccadax beccadax Feb 23, 2019

Choose a reason for hiding this comment

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

This catch-all would cover many of the cases above; I mention them separately because I expect that build system engineers and others who don't often work in the Swift compiler may need to read this code, and I want to be extra-clear about what's supported. (A few other parts of this implementation, like the use of arguably unnecessary helper functions with wordy names, are also influenced by that.)

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to add a comment about why you mention them separately so that future maintainers will know why this is the case and how to add support for additional architectures.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would you want to not have a default, and conditionalize calling this based on the OS instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This already is effectively conditionalized on Triple::isOSDarwin(). I just couldn't think of a good behavior if we didn't recognize the architecture (or, in other functions, the OS or environment)—this isn't a great place to diagnose an error, there's no sensible fixed default, and other parts of the compiler will diagnose anything the compiler is truly incapable of handling.

(I suppose one answer is that we ought to centralize our many disparate notions of OS, architecture, and environment into a single source of truth, but that's not something I want to tackle in this PR.)

}

static StringRef
getVendorForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) {
// We unconditionally normalize to "apple" because it's relatively common for
// build systems to omit the vendor name or use an incorrect one like
// "unknown". Most parts of the compiler ignore the vendor, so you might not
// notice such a mistake.
//
// Please don't depend on this behavior--specify 'apple' if you're building
// for an Apple platform.

assert(triple.isOSDarwin() &&
"shouldn't normalize non-Darwin triple to 'apple'");

return "apple";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although we previously considered not normalizing the vendor, I discovered later that the Swift benchmark suite currently builds with an empty vendor field. I plan to fix that, but it made me rethink that decision.

Copy link
Contributor

Choose a reason for hiding this comment

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

There should probably be a comment explaining why you're doing this.

}

static StringRef
getOSForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) {
auto tripleOSName = triple.getOSName();

// Truncate the OS name before the first digit. "Digit" here is ASCII '0'-'9'.
auto tripleOSNameNoVersion = tripleOSName.take_until(llvm::isDigit);

return llvm::StringSwitch<StringRef>(tripleOSNameNoVersion)
.Cases("macos", "macosx", "darwin", "macos")
.Cases("unknown", "", "unknown")
// These values are also supported, but are handled by the default case below:
// .Case ("ios", "ios")
// .Case ("tvos", "tvos")
// .Case ("watchos", "watchos")
.Default(tripleOSNameNoVersion);
}

static Optional<StringRef>
getEnvironmentForAppleTargetSpecificModuleTriple(const llvm::Triple &triple) {
auto tripleEnvironment = triple.getEnvironmentName();

// If the environment is empty, infer a "simulator" environment based on the
// OS and architecture combination. This feature is deprecated and exists for
// backwards compatibility only; build systems should pass the "simulator"
// environment explicitly if they know they're building for a simulator.
if (tripleEnvironment == "" && swift::tripleIsAnySimulator(triple))
return StringRef("simulator");

return llvm::StringSwitch<Optional<StringRef>>(tripleEnvironment)
.Cases("unknown", "", None)
// These values are also supported, but are handled by the default case below:
// .Case ("simulator", StringRef("simulator"))
.Default(tripleEnvironment);
}

llvm::Triple swift::getTargetSpecificModuleTriple(const llvm::Triple &triple) {
// isOSDarwin() returns true for all Darwin-style OSes, including macOS, iOS,
// etc.
if (triple.isOSDarwin()) {
StringRef newArch = getArchForAppleTargetSpecificModuleTriple(triple);

StringRef newVendor = getVendorForAppleTargetSpecificModuleTriple(triple);

StringRef newOS = getOSForAppleTargetSpecificModuleTriple(triple);

Optional<StringRef> newEnvironment =
getEnvironmentForAppleTargetSpecificModuleTriple(triple);

if (!newEnvironment)
// Generate an arch-vendor-os triple.
return llvm::Triple(newArch, newVendor, newOS);

// Generate an arch-vendor-os-environment triple.
return llvm::Triple(newArch, newVendor, newOS, *newEnvironment);
}

// Other platforms get no normalization.
return triple;
}

Loading