Skip to content

Commit f2cf55a

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 1426980 commit f2cf55a

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
@@ -50,6 +50,7 @@ namespace clang {
5050
class VisibleDeclConsumer;
5151
class DeclarationName;
5252
class CompilerInvocation;
53+
class TargetOptions;
5354
namespace tooling {
5455
namespace dependencies {
5556
struct ModuleDeps;
@@ -173,7 +174,7 @@ class ClangImporter final : public ClangModuleLoader {
173174
DWARFImporterDelegate *dwarfImporterDelegate = nullptr);
174175

175176
static std::vector<std::string>
176-
getClangArguments(ASTContext &ctx);
177+
getClangArguments(ASTContext &ctx, bool ignoreClangTarget = false);
177178

178179
static std::unique_ptr<clang::CompilerInvocation>
179180
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
@@ -718,11 +718,11 @@ getEmbedBitcodeInvocationArguments(std::vector<std::string> &invocationArgStrs,
718718
void
719719
importer::addCommonInvocationArguments(
720720
std::vector<std::string> &invocationArgStrs,
721-
ASTContext &ctx) {
721+
ASTContext &ctx, bool ignoreClangTarget) {
722722
using ImporterImpl = ClangImporter::Implementation;
723723
llvm::Triple triple = ctx.LangOpts.Target;
724724
// Use clang specific target triple if given.
725-
if (ctx.LangOpts.ClangTarget.has_value()) {
725+
if (ctx.LangOpts.ClangTarget.has_value() && !ignoreClangTarget) {
726726
triple = ctx.LangOpts.ClangTarget.value();
727727
}
728728
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
@@ -970,7 +970,7 @@ Optional<std::string> ClangImporter::getOrCreatePCH(
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,
@@ -1872,7 +1893,7 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath,
18721893
clang::Module *m;
18731894
auto &ctx = Impl.getClangASTContext();
18741895
auto &lo = ctx.getLangOpts();
1875-
auto &ti = getTargetInfo();
1896+
auto &ti = getModuleAvailabilityTarget();
18761897

18771898
auto available = clangModule->isAvailable(lo, ti, r, mh, m);
18781899
if (!available)
@@ -3687,10 +3708,14 @@ StringRef ClangModuleUnit::getLoadedFilename() const {
36873708
return StringRef();
36883709
}
36893710

3690-
clang::TargetInfo &ClangImporter::getTargetInfo() const {
3711+
clang::TargetInfo &ClangImporter::getModuleAvailabilityTarget() const {
36913712
return Impl.Instance->getTarget();
36923713
}
36933714

3715+
clang::TargetInfo &ClangImporter::getTargetInfo() const {
3716+
return *Impl.getSwiftTargetInfo();
3717+
}
3718+
36943719
clang::ASTContext &ClangImporter::getClangASTContext() const {
36953720
return Impl.getClangASTContext();
36963721
}
@@ -3720,8 +3745,8 @@ clang::Sema &ClangImporter::getClangSema() const {
37203745
return Impl.getClangSema();
37213746
}
37223747

3723-
clang::CodeGenOptions &ClangImporter::getClangCodeGenOpts() const {
3724-
return Impl.getClangCodeGenOpts();
3748+
clang::CodeGenOptions &ClangImporter::getCodeGenOpts() const {
3749+
return *Impl.getSwiftCodeGenOptions();
37253750
}
37263751

37273752
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

@@ -1814,7 +1838,8 @@ void getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
18141838

18151839
/// Add command-line arguments common to all imports of Clang code.
18161840
void addCommonInvocationArguments(std::vector<std::string> &invocationArgStrs,
1817-
ASTContext &ctx);
1841+
ASTContext &ctx,
1842+
bool ignoreClangTarget);
18181843

18191844
/// Finds a particular kind of nominal by looking through typealiases.
18201845
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)