Skip to content

Commit 7a7339e

Browse files
committed
[Explicit Module Builds] Ensure IRGen uses Swift compiler's target triple when '-clang-target' is set.
The Clang importer's Clang instance may be configured with a different (higher) OS version than the compilation target itself in order to be able to load pre-compiled Clang modules that are aligned with the broader SDK, and match the SDK deployment target against which Swift modules are also built. In this case, we must use the Swift compiler's OS version triple in order to generate the binary as-requested. This change makes 'ClangImporter' 'Implementation' keep track of a distinct 'TargetInfo' and 'CodeGenOpts' containers that are meant to be used by clients in IRGen. When '-clang-target' is not set, they are defined to be copies of the 'ClangImporter's built-in module-loading Clang instance. When '-clang-target' is set, they are configured with the Swift compilation's target triple and OS version (but otherwise identical) instead. To distinguish IRGen clients from module loading clients, 'getModuleAvailabilityTarget' is added for module loading clients of 'ClangImporter'. The notion of using a different triple for loading Clang modules arises for the following reason: - Swift is able to load Swift modules built against a different target triple than the source module that is being compiled. Swift relies on availability annotations on the API within the loaded modules to ensure that compilation for the current target only uses appropriately-available API from its dependencies. - Clang, in contrast, requires that compilation only ever load modules (.pcm) that are precisely aligned to the current source compilation. Because the target triple (OS version in particular) between Swift source compilation and Swift dependency module compilation may differ, this would otherwise result in builtin multiple copies of the same Clang module, against different OS versions, once for each different triple in the build graph. Instead, with Explicitly-Built Modules, Swift sets a '-clang-target' argument that ensures that all Clang modules participating in the build are built against the SDK deployment target, matching the Swift modules in the SDK, which allows them to expose a maximally-available API surface as required by potentially-depending Swift modules' target OS version. -------------------------------------------- For example: Suppose we are building a source module 'Foo', targeting 'macosx10.0', using an SDK with a deployment target of 'macosx12.0'. Swift modules in said SDK will be built for 'macosx12.0' (as hard-coded in their textual interfaces), meaning they may reference symbols expected to be present in dependency Clang modules at that target OS version. Suppose the source module 'Foo' depends on Swift module 'Bar', which then depends on Clang module `Baz`. 'Bar' must be built targeting 'macosx12.0' (SDK-matching deployment target is hard-coded into its textual interface). Which means that 'Bar' expects 'Baz' to expose symbols that may only be available when targeting at least 'macosx12.0'. e.g. 'Baz' may have symbols guarded with '__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_12_0'. For this reason, we use '-clang-target' to ensure 'Baz' is built targeting 'macosx12.0', and can be loaded by both 'Foo' and 'Bar'. As a result, we cannot direclty use the Clang instance's target triple here and must check if we need to instead use the triple of the Swift compiler instance. Resolves rdar://109228963
1 parent 8397fbd commit 7a7339e

File tree

5 files changed

+111
-26
lines changed

5 files changed

+111
-26
lines changed

include/swift/AST/ClangModuleLoader.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,21 @@ class ClangModuleLoader : public ModuleLoader {
124124
using ModuleLoader::ModuleLoader;
125125

126126
public:
127-
virtual clang::TargetInfo &getTargetInfo() const = 0;
127+
/// This module loader's Clang instance may be configured with a different
128+
/// (higher) OS version than the compilation target itself in order to be able
129+
/// to load pre-compiled Clang modules that are aligned with the broader SDK,
130+
/// and match the SDK deployment target against which Swift modules are also
131+
/// built.
132+
///
133+
/// In this case, we must use the Swift compiler's OS version triple when
134+
/// performing codegen, and the importer's Clang instance OS version triple
135+
/// during module loading. `getModuleAvailabilityTarget` is for module-loading
136+
/// clients only, and uses the latter.
137+
///
138+
/// (The implementing `ClangImporter` class maintains separate Target info
139+
/// for use by IRGen/CodeGen clients)
140+
virtual clang::TargetInfo &getModuleAvailabilityTarget() const = 0;
141+
128142
virtual clang::ASTContext &getClangASTContext() const = 0;
129143
virtual clang::Preprocessor &getClangPreprocessor() const = 0;
130144
virtual clang::Sema &getClangSema() const = 0;

include/swift/ClangImporter/ClangImporter.h

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ namespace clang {
4949
class VisibleDeclConsumer;
5050
class DeclarationName;
5151
class CompilerInvocation;
52+
class TargetOptions;
5253
namespace tooling {
5354
namespace dependencies {
5455
struct ModuleDeps;
@@ -172,7 +173,7 @@ class ClangImporter final : public ClangModuleLoader {
172173
DWARFImporterDelegate *dwarfImporterDelegate = nullptr);
173174

174175
static std::vector<std::string>
175-
getClangArguments(ASTContext &ctx);
176+
getClangArguments(ASTContext &ctx, bool ignoreClangTarget = false);
176177

177178
static std::unique_ptr<clang::CompilerInvocation>
178179
createClangInvocation(ClangImporter *importer,
@@ -445,13 +446,33 @@ class ClangImporter final : public ClangModuleLoader {
445446
StringRef moduleName,
446447
ModuleDependencyKind moduleKind,
447448
ModuleDependenciesCache &cache);
448-
449-
clang::TargetInfo &getTargetInfo() const override;
449+
clang::TargetInfo &getModuleAvailabilityTarget() const override;
450450
clang::ASTContext &getClangASTContext() const override;
451451
clang::Preprocessor &getClangPreprocessor() const override;
452452
clang::Sema &getClangSema() const override;
453453
const clang::CompilerInstance &getClangInstance() const override;
454-
clang::CodeGenOptions &getClangCodeGenOpts() const;
454+
455+
/// ClangImporter's Clang instance may be configured with a different
456+
/// (higher) OS version than the compilation target itself in order to be able
457+
/// to load pre-compiled Clang modules that are aligned with the broader SDK,
458+
/// and match the SDK deployment target against which Swift modules are also
459+
/// built.
460+
///
461+
/// In this case, we must use the Swift compiler's OS version triple when
462+
/// performing codegen, and the importer's Clang instance OS version triple
463+
/// during module loading.
464+
///
465+
/// `ClangImporter`'s `Implementation` keeps track of a distinct `TargetInfo`
466+
/// and `CodeGenOpts` containers that are meant to be used by clients in
467+
/// IRGen. When a separate `-clang-target` is not set, they are defined to be
468+
/// copies of the `ClangImporter`'s built-in module-loading Clang instance.
469+
/// When `-clang-target` is set, they are configured with the Swift
470+
/// compilation's target triple and OS version (but otherwise identical)
471+
/// instead. To distinguish IRGen clients from module loading clients,
472+
/// `getModuleAvailabilityTarget` should be used instead by module-loading
473+
/// clients.
474+
clang::TargetInfo &getTargetInfo() const;
475+
clang::CodeGenOptions &getCodeGenOpts() const;
455476

456477
std::string getClangModuleHash() const;
457478

lib/ClangImporter/ClangImporter.cpp

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -716,11 +716,11 @@ getEmbedBitcodeInvocationArguments(std::vector<std::string> &invocationArgStrs,
716716
void
717717
importer::addCommonInvocationArguments(
718718
std::vector<std::string> &invocationArgStrs,
719-
ASTContext &ctx) {
719+
ASTContext &ctx, bool ignoreClangTarget) {
720720
using ImporterImpl = ClangImporter::Implementation;
721721
llvm::Triple triple = ctx.LangOpts.Target;
722722
// Use clang specific target triple if given.
723-
if (ctx.LangOpts.ClangTarget.has_value()) {
723+
if (ctx.LangOpts.ClangTarget.has_value() && !ignoreClangTarget) {
724724
triple = ctx.LangOpts.ClangTarget.value();
725725
}
726726
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
@@ -970,7 +970,7 @@ ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
970970
}
971971

972972
std::vector<std::string>
973-
ClangImporter::getClangArguments(ASTContext &ctx) {
973+
ClangImporter::getClangArguments(ASTContext &ctx, bool ignoreClangTarget) {
974974
std::vector<std::string> invocationArgStrs;
975975
// Clang expects this to be like an actual command line. So we need to pass in
976976
// "clang" for argv[0]
@@ -991,7 +991,7 @@ ClangImporter::getClangArguments(ASTContext &ctx) {
991991
getEmbedBitcodeInvocationArguments(invocationArgStrs, ctx);
992992
break;
993993
}
994-
addCommonInvocationArguments(invocationArgStrs, ctx);
994+
addCommonInvocationArguments(invocationArgStrs, ctx, ignoreClangTarget);
995995
return invocationArgStrs;
996996
}
997997

@@ -1094,15 +1094,6 @@ ClangImporter::create(ASTContext &ctx,
10941094
std::unique_ptr<ClangImporter> importer{
10951095
new ClangImporter(ctx, tracker, dwarfImporterDelegate)};
10961096
auto &importerOpts = ctx.ClangImporterOpts;
1097-
importer->Impl.ClangArgs = getClangArguments(ctx);
1098-
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
1099-
if (importerOpts.DumpClangDiagnostics) {
1100-
llvm::errs() << "'";
1101-
llvm::interleave(
1102-
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
1103-
[] { llvm::errs() << "' '"; });
1104-
llvm::errs() << "'\n";
1105-
}
11061097

11071098
if (isPCHFilenameExtension(importerOpts.BridgingHeader)) {
11081099
importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader);
@@ -1142,6 +1133,15 @@ ClangImporter::create(ASTContext &ctx,
11421133

11431134
// Create a new Clang compiler invocation.
11441135
{
1136+
importer->Impl.ClangArgs = getClangArguments(ctx);
1137+
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
1138+
if (importerOpts.DumpClangDiagnostics) {
1139+
llvm::errs() << "'";
1140+
llvm::interleave(
1141+
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
1142+
[] { llvm::errs() << "' '"; });
1143+
llvm::errs() << "'\n";
1144+
}
11451145
importer->Impl.Invocation = createClangInvocation(
11461146
importer.get(), importerOpts, VFS, invocationArgStrs);
11471147
if (!importer->Impl.Invocation)
@@ -1217,6 +1217,27 @@ ClangImporter::create(ASTContext &ctx,
12171217
clang::SourceLocation());
12181218
clangDiags.setFatalsAsError(ctx.Diags.getShowDiagnosticsAfterFatalError());
12191219

1220+
// Use Clang to configure/save options for Swift IRGen/CodeGen
1221+
if (ctx.LangOpts.ClangTarget.has_value()) {
1222+
// If '-clang-target' is set, create a mock invocation with the Swift triple
1223+
// to configure CodeGen and Target options for Swift compilation.
1224+
auto swiftTargetClangArgs = getClangArguments(ctx, true);
1225+
ArrayRef<std::string> invocationArgStrs = swiftTargetClangArgs;
1226+
auto swiftTargetClangInvocation = createClangInvocation(
1227+
importer.get(), importerOpts, VFS, invocationArgStrs);
1228+
if (!swiftTargetClangInvocation)
1229+
return nullptr;
1230+
importer->Impl.setSwiftTargetInfo(clang::TargetInfo::CreateTargetInfo(
1231+
clangDiags, swiftTargetClangInvocation->TargetOpts));
1232+
importer->Impl.setSwiftCodeGenOptions(new clang::CodeGenOptions(
1233+
swiftTargetClangInvocation->getCodeGenOpts()));
1234+
} else {
1235+
// Just use the existing Invocation's directly
1236+
importer->Impl.setSwiftTargetInfo(clang::TargetInfo::CreateTargetInfo(
1237+
clangDiags, importer->Impl.Invocation->TargetOpts));
1238+
importer->Impl.setSwiftCodeGenOptions(
1239+
new clang::CodeGenOptions(importer->Impl.Invocation->getCodeGenOpts()));
1240+
}
12201241

12211242
// Create the associated action.
12221243
importer->Impl.Action.reset(new ParsingAction(ctx, *importer,
@@ -1875,7 +1896,7 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath,
18751896
clang::Module *m;
18761897
auto &ctx = Impl.getClangASTContext();
18771898
auto &lo = ctx.getLangOpts();
1878-
auto &ti = getTargetInfo();
1899+
auto &ti = getModuleAvailabilityTarget();
18791900

18801901
auto available = clangModule->isAvailable(lo, ti, r, mh, m);
18811902
if (!available)
@@ -3683,10 +3704,14 @@ StringRef ClangModuleUnit::getLoadedFilename() const {
36833704
return StringRef();
36843705
}
36853706

3686-
clang::TargetInfo &ClangImporter::getTargetInfo() const {
3707+
clang::TargetInfo &ClangImporter::getModuleAvailabilityTarget() const {
36873708
return Impl.Instance->getTarget();
36883709
}
36893710

3711+
clang::TargetInfo &ClangImporter::getTargetInfo() const {
3712+
return *Impl.getSwiftTargetInfo();
3713+
}
3714+
36903715
clang::ASTContext &ClangImporter::getClangASTContext() const {
36913716
return Impl.getClangASTContext();
36923717
}
@@ -3716,8 +3741,8 @@ clang::Sema &ClangImporter::getClangSema() const {
37163741
return Impl.getClangSema();
37173742
}
37183743

3719-
clang::CodeGenOptions &ClangImporter::getClangCodeGenOpts() const {
3720-
return Impl.getClangCodeGenOpts();
3744+
clang::CodeGenOptions &ClangImporter::getCodeGenOpts() const {
3745+
return *Impl.getSwiftCodeGenOptions();
37213746
}
37223747

37233748
std::string ClangImporter::getClangModuleHash() const {

lib/ClangImporter/ImporterImpl.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "clang/AST/DeclVisitor.h"
4040
#include "clang/AST/RecursiveASTVisitor.h"
4141
#include "clang/Basic/IdentifierTable.h"
42+
#include "clang/Basic/TargetInfo.h"
4243
#include "clang/Frontend/CompilerInstance.h"
4344
#include "clang/Serialization/ModuleFileExtension.h"
4445
#include "llvm/ADT/APSInt.h"
@@ -588,6 +589,29 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
588589
return Instance.get();
589590
}
590591

592+
private:
593+
/// The Importer may be configured to load modules of a different OS Version
594+
/// than the underlying Swift compilation. This is the `TargetOptions`
595+
/// corresponding to the instantiating Swift compilation's triple. These are
596+
/// to be used by all IRGen/CodeGen clients of `ClangImporter`.
597+
std::unique_ptr<clang::TargetInfo> CodeGenTargetInfo;
598+
std::unique_ptr<clang::CodeGenOptions> CodeGenOpts;
599+
600+
public:
601+
void setSwiftTargetInfo(clang::TargetInfo *SwiftTargetInfo) {
602+
CodeGenTargetInfo.reset(SwiftTargetInfo);
603+
}
604+
clang::TargetInfo *getSwiftTargetInfo() const {
605+
return CodeGenTargetInfo.get();
606+
}
607+
608+
void setSwiftCodeGenOptions(clang::CodeGenOptions *SwiftCodeGenOpts) {
609+
CodeGenOpts.reset(SwiftCodeGenOpts);
610+
}
611+
clang::CodeGenOptions *getSwiftCodeGenOptions() const {
612+
return CodeGenOpts.get();
613+
}
614+
591615
private:
592616
/// Generation number that is used for crude versioning.
593617
///
@@ -835,7 +859,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
835859
return Instance->getPreprocessor();
836860
}
837861

838-
clang::CodeGenOptions &getClangCodeGenOpts() const {
862+
clang::CodeGenOptions &getCodeGenOpts() const {
839863
return Instance->getCodeGenOpts();
840864
}
841865

@@ -1811,7 +1835,8 @@ void getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
18111835

18121836
/// Add command-line arguments common to all imports of Clang code.
18131837
void addCommonInvocationArguments(std::vector<std::string> &invocationArgStrs,
1814-
ASTContext &ctx);
1838+
ASTContext &ctx,
1839+
bool ignoreClangTarget);
18151840

18161841
/// Finds a particular kind of nominal by looking through typealiases.
18171842
template <typename T>

lib/IRGen/IRGenModule.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static llvm::StructType *createStructType(IRGenModule &IGM,
8686
}
8787

8888
static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context,
89-
llvm::LLVMContext &LLVMContext,
89+
llvm::LLVMContext &LLVMContext,
9090
const IRGenOptions &Opts,
9191
StringRef ModuleName,
9292
StringRef PD) {
@@ -95,7 +95,7 @@ static clang::CodeGenerator *createClangCodeGenerator(ASTContext &Context,
9595
assert(Importer && "No clang module loader!");
9696
auto &ClangContext = Importer->getClangASTContext();
9797

98-
auto &CGO = Importer->getClangCodeGenOpts();
98+
auto &CGO = Importer->getCodeGenOpts();
9999
if (CGO.OpaquePointers) {
100100
LLVMContext.setOpaquePointers(true);
101101
} else {

0 commit comments

Comments
 (0)