Skip to content

Commit ec70b62

Browse files
authored
Merge pull request #28107 from allevato/emit-pcm
Add driver flag to precompile Swift-compatible explicit Clang modules.
2 parents 2f78afa + 2c7b518 commit ec70b62

File tree

16 files changed

+310
-43
lines changed

16 files changed

+310
-43
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ ERROR(bridging_header_pch_error,Fatal,
6464
"failed to emit precompiled header '%0' for bridging header '%1'",
6565
(StringRef, StringRef))
6666

67+
ERROR(emit_pcm_error,Fatal,
68+
"failed to emit precompiled module '%0' for module map '%1'",
69+
(StringRef, StringRef))
70+
ERROR(dump_pcm_error,Fatal,
71+
"failed to dump precompiled module '%0'", (StringRef))
72+
6773
WARNING(invalid_swift_name_method,none,
6874
"too %select{few|many}0 parameters in swift_name attribute (expected %1; "
6975
"got %2)", (bool, unsigned, unsigned))

include/swift/ClangImporter/ClangImporter.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ class ClangImporter final : public ClangModuleLoader {
112112
DependencyTracker *tracker,
113113
DWARFImporterDelegate *dwarfImporterDelegate);
114114

115+
/// Creates a clone of Clang importer's compiler instance that has been
116+
/// configured for operations on precompiled outputs (either emitting a
117+
/// precompiled header, emitting a precompiled module, or dumping a
118+
/// precompiled module).
119+
///
120+
/// The caller of this method should set any action-specific invocation
121+
/// options (like FrontendOptions::ProgramAction, input files, and output
122+
/// paths), then create the appropriate FrontendAction and execute it.
123+
std::unique_ptr<clang::CompilerInstance>
124+
cloneCompilerInstanceForPrecompiling();
125+
115126
public:
116127
/// Create a new Clang importer that can import a suitable Clang
117128
/// module into the given ASTContext.
@@ -333,6 +344,19 @@ class ClangImporter final : public ClangModuleLoader {
333344
/// if we need to persist a PCH for later reuse.
334345
bool canReadPCH(StringRef PCHFilename);
335346

347+
/// Makes a temporary replica of the ClangImporter's CompilerInstance, reads a
348+
/// module map into the replica and emits a PCM file for one of the modules it
349+
/// declares. Delegates to clang for everything except construction of the
350+
/// replica.
351+
bool emitPrecompiledModule(StringRef moduleMapPath, StringRef moduleName,
352+
StringRef outputPath);
353+
354+
/// Makes a temporary replica of the ClangImporter's CompilerInstance and
355+
/// dumps information about a PCM file (assumed to be generated by -emit-pcm
356+
/// or in the Swift module cache). Delegates to clang for everything except
357+
/// construction of the replica.
358+
bool dumpPrecompiledModule(StringRef modulePath, StringRef outputPath);
359+
336360
const clang::Module *getClangOwningModule(ClangNode Node) const;
337361
bool hasTypedef(const clang::Decl *typeDecl) const;
338362

include/swift/ClangImporter/ClangImporterOptions.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ class ClangImporterOptions {
6060
/// Swift code.
6161
Normal,
6262
/// Set up Clang for backend compilation only.
63-
EmbedBitcode
63+
EmbedBitcode,
64+
/// Set up Clang to emit a precompiled module from a C/Objective-C module
65+
/// map or dump debugging info about a precompiled module.
66+
PrecompiledModule
6467
};
6568

6669
/// Controls how Clang is initially set up.

include/swift/Frontend/Frontend.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ class CompilerInvocation {
203203

204204
void setRuntimeResourcePath(StringRef Path);
205205

206+
/// Computes the runtime resource path relative to the given Swift
207+
/// executable.
208+
static void computeRuntimeResourcePathFromExecutablePath(
209+
StringRef mainExecutablePath,
210+
llvm::SmallString<128> &runtimeResourcePath);
211+
206212
void setSDKPath(const std::string &Path);
207213

208214
StringRef getSDKPath() const {

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class FrontendOptions {
126126
EmitObject, ///< Emit object file
127127

128128
DumpTypeInfo, ///< Dump IRGen type info
129+
130+
EmitPCM, ///< Emit precompiled Clang module from a module map
131+
DumpPCM, ///< Dump information about a precompiled Clang module
129132
};
130133

131134
/// Indicates the action the user requested that the frontend perform.

include/swift/Option/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,9 @@ def emit_sibgen : Flag<["-"], "emit-sibgen">,
748748
def emit_imported_modules : Flag<["-"], "emit-imported-modules">,
749749
HelpText<"Emit a list of the imported modules">, ModeOpt,
750750
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
751+
def emit_pcm : Flag<["-"], "emit-pcm">,
752+
HelpText<"Emit a precompiled Clang module from a module map">, ModeOpt,
753+
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
751754

752755
def c : Flag<["-"], "c">, Alias<emit_object>,
753756
Flags<[FrontendOption, NoInteractiveOption]>, ModeOpt;
@@ -792,6 +795,10 @@ def print_ast : Flag<["-"], "print-ast">,
792795
HelpText<"Parse and type-check input file(s) and pretty print AST(s)">,
793796
ModeOpt,
794797
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
798+
def dump_pcm : Flag<["-"], "dump-pcm">,
799+
HelpText<"Dump debugging information about a precompiled Clang module">,
800+
ModeOpt,
801+
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
795802

796803
// Other Modes
797804
def repl : Flag<["-"], "repl">,

lib/ClangImporter/ClangImporter.cpp

Lines changed: 137 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,7 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts,
902902
invocationArgStrs.push_back("clang");
903903
switch (importerOpts.Mode) {
904904
case ClangImporterOptions::Modes::Normal:
905+
case ClangImporterOptions::Modes::PrecompiledModule:
905906
getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts);
906907
break;
907908
case ClangImporterOptions::Modes::EmbedBitcode:
@@ -1062,7 +1063,17 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts,
10621063
if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode)
10631064
return importer;
10641065

1066+
// ClangImporter always sets this in Normal mode, so we need to make sure to
1067+
// set it before bailing out early when configuring ClangImporter for
1068+
// precompiled modules. This is not a benign langopt, so forgetting this (for
1069+
// example, if we combined the early exit below with the one above) would make
1070+
// the compiler instance used to emit PCMs incompatible with the one used to
1071+
// read them later.
10651072
instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true;
1073+
1074+
if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule)
1075+
return importer;
1076+
10661077
bool canBegin = action->BeginSourceFile(instance,
10671078
instance.getFrontendOpts().Inputs[0]);
10681079
if (!canBegin)
@@ -1446,43 +1457,82 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
14461457
return result;
14471458
}
14481459

1449-
bool
1450-
ClangImporter::emitBridgingPCH(StringRef headerPath,
1451-
StringRef outputPCHPath) {
1460+
/// Returns the appropriate source input language based on language options.
1461+
static clang::Language getLanguageFromOptions(
1462+
const clang::LangOptions *LangOpts) {
1463+
if (LangOpts->OpenCL)
1464+
return clang::Language::OpenCL;
1465+
if (LangOpts->CUDA)
1466+
return clang::Language::CUDA;
1467+
if (LangOpts->ObjC)
1468+
return LangOpts->CPlusPlus ?
1469+
clang::Language::ObjCXX : clang::Language::ObjC;
1470+
return LangOpts->CPlusPlus ? clang::Language::CXX : clang::Language::C;
1471+
}
1472+
1473+
/// Wraps the given frontend action in an index data recording action if the
1474+
/// frontend options have an index store path specified.
1475+
static
1476+
std::unique_ptr<clang::FrontendAction> wrapActionForIndexingIfEnabled(
1477+
const clang::FrontendOptions &FrontendOpts,
1478+
std::unique_ptr<clang::FrontendAction> action) {
1479+
if (!FrontendOpts.IndexStorePath.empty()) {
1480+
return clang::index::createIndexDataRecordingAction(
1481+
FrontendOpts, std::move(action));
1482+
}
1483+
return action;
1484+
}
1485+
1486+
std::unique_ptr<clang::CompilerInstance>
1487+
ClangImporter::cloneCompilerInstanceForPrecompiling() {
14521488
auto invocation =
14531489
std::make_shared<clang::CompilerInvocation>(*Impl.Invocation);
1454-
invocation->getFrontendOpts().DisableFree = false;
1455-
invocation->getFrontendOpts().Inputs.clear();
1456-
invocation->getFrontendOpts().Inputs.push_back(
1457-
clang::FrontendInputFile(headerPath, clang::Language::ObjC));
1458-
invocation->getFrontendOpts().OutputFile = outputPCHPath;
1459-
invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH;
1460-
invocation->getPreprocessorOpts().resetNonModularOptions();
1461-
invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true;
1462-
invocation->getLangOpts()->CacheGeneratedPCH = true;
14631490

1464-
clang::CompilerInstance emitInstance(
1491+
auto &PPOpts = invocation->getPreprocessorOpts();
1492+
PPOpts.resetNonModularOptions();
1493+
1494+
auto &FrontendOpts = invocation->getFrontendOpts();
1495+
FrontendOpts.DisableFree = false;
1496+
FrontendOpts.Inputs.clear();
1497+
1498+
auto clonedInstance = llvm::make_unique<clang::CompilerInstance>(
14651499
Impl.Instance->getPCHContainerOperations(),
14661500
&Impl.Instance->getModuleCache());
1467-
emitInstance.setInvocation(std::move(invocation));
1468-
emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(),
1469-
/*ShouldOwnClient=*/false);
1501+
clonedInstance->setInvocation(std::move(invocation));
1502+
clonedInstance->createDiagnostics(&Impl.Instance->getDiagnosticClient(),
1503+
/*ShouldOwnClient=*/false);
14701504

14711505
clang::FileManager &fileManager = Impl.Instance->getFileManager();
1472-
emitInstance.setFileManager(&fileManager);
1473-
emitInstance.createSourceManager(fileManager);
1474-
emitInstance.setTarget(&Impl.Instance->getTarget());
1506+
clonedInstance->setFileManager(&fileManager);
1507+
clonedInstance->createSourceManager(fileManager);
1508+
clonedInstance->setTarget(&Impl.Instance->getTarget());
14751509

1476-
std::unique_ptr<clang::FrontendAction> action;
1477-
action.reset(new clang::GeneratePCHAction());
1478-
if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) {
1479-
action = clang::index::
1480-
createIndexDataRecordingAction(emitInstance.getFrontendOpts(),
1481-
std::move(action));
1482-
}
1483-
emitInstance.ExecuteAction(*action);
1510+
return clonedInstance;
1511+
}
1512+
1513+
bool
1514+
ClangImporter::emitBridgingPCH(StringRef headerPath,
1515+
StringRef outputPCHPath) {
1516+
auto emitInstance = cloneCompilerInstanceForPrecompiling();
1517+
auto &invocation = emitInstance->getInvocation();
1518+
1519+
auto LangOpts = invocation.getLangOpts();
1520+
LangOpts->NeededByPCHOrCompilationUsesPCH = true;
1521+
LangOpts->CacheGeneratedPCH = true;
1522+
1523+
auto language = getLanguageFromOptions(LangOpts);
1524+
auto inputFile = clang::FrontendInputFile(headerPath, language);
1525+
1526+
auto &FrontendOpts = invocation.getFrontendOpts();
1527+
FrontendOpts.Inputs = {inputFile};
1528+
FrontendOpts.OutputFile = outputPCHPath;
1529+
FrontendOpts.ProgramAction = clang::frontend::GeneratePCH;
14841530

1485-
if (emitInstance.getDiagnostics().hasErrorOccurred()) {
1531+
auto action = wrapActionForIndexingIfEnabled(
1532+
FrontendOpts, llvm::make_unique<clang::GeneratePCHAction>());
1533+
emitInstance->ExecuteAction(*action);
1534+
1535+
if (emitInstance->getDiagnostics().hasErrorOccurred()) {
14861536
Impl.SwiftContext.Diags.diagnose({},
14871537
diag::bridging_header_pch_error,
14881538
outputPCHPath, headerPath);
@@ -1491,6 +1541,65 @@ ClangImporter::emitBridgingPCH(StringRef headerPath,
14911541
return false;
14921542
}
14931543

1544+
bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath,
1545+
StringRef moduleName,
1546+
StringRef outputPath) {
1547+
auto emitInstance = cloneCompilerInstanceForPrecompiling();
1548+
auto &invocation = emitInstance->getInvocation();
1549+
1550+
auto LangOpts = invocation.getLangOpts();
1551+
LangOpts->setCompilingModule(clang::LangOptions::CMK_ModuleMap);
1552+
LangOpts->ModuleName = moduleName;
1553+
LangOpts->CurrentModule = LangOpts->ModuleName;
1554+
1555+
auto language = getLanguageFromOptions(LangOpts);
1556+
auto inputFile = clang::FrontendInputFile(
1557+
moduleMapPath, clang::InputKind(
1558+
language, clang::InputKind::ModuleMap, false));
1559+
1560+
auto &FrontendOpts = invocation.getFrontendOpts();
1561+
FrontendOpts.Inputs = {inputFile};
1562+
FrontendOpts.OriginalModuleMap = moduleMapPath;
1563+
FrontendOpts.OutputFile = outputPath;
1564+
FrontendOpts.ProgramAction = clang::frontend::GenerateModule;
1565+
1566+
auto action = wrapActionForIndexingIfEnabled(
1567+
FrontendOpts,
1568+
llvm::make_unique<clang::GenerateModuleFromModuleMapAction>());
1569+
emitInstance->ExecuteAction(*action);
1570+
1571+
if (emitInstance->getDiagnostics().hasErrorOccurred()) {
1572+
Impl.SwiftContext.Diags.diagnose({},
1573+
diag::emit_pcm_error,
1574+
outputPath, moduleMapPath);
1575+
return true;
1576+
}
1577+
return false;
1578+
}
1579+
1580+
bool ClangImporter::dumpPrecompiledModule(StringRef modulePath,
1581+
StringRef outputPath) {
1582+
auto dumpInstance = cloneCompilerInstanceForPrecompiling();
1583+
auto &invocation = dumpInstance->getInvocation();
1584+
1585+
auto inputFile = clang::FrontendInputFile(
1586+
modulePath, clang::InputKind(
1587+
clang::Language::Unknown, clang::InputKind::Precompiled, false));
1588+
1589+
auto &FrontendOpts = invocation.getFrontendOpts();
1590+
FrontendOpts.Inputs = {inputFile};
1591+
FrontendOpts.OutputFile = outputPath;
1592+
1593+
auto action = llvm::make_unique<clang::DumpModuleInfoAction>();
1594+
dumpInstance->ExecuteAction(*action);
1595+
1596+
if (dumpInstance->getDiagnostics().hasErrorOccurred()) {
1597+
Impl.SwiftContext.Diags.diagnose({}, diag::dump_pcm_error, modulePath);
1598+
return true;
1599+
}
1600+
return false;
1601+
}
1602+
14941603
void ClangImporter::collectVisibleTopLevelModuleNames(
14951604
SmallVectorImpl<Identifier> &names) const {
14961605
SmallVector<clang::Module *, 32> Modules;

lib/Driver/Driver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
14101410
OI.CompilerOutputType = file_types::TY_PCH;
14111411
break;
14121412

1413+
case options::OPT_emit_pcm:
1414+
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
1415+
OI.CompilerOutputType = file_types::TY_ClangModuleFile;
1416+
break;
1417+
14131418
case options::OPT_emit_imported_modules:
14141419
OI.CompilerOutputType = file_types::TY_ImportedModules;
14151420
// We want the imported modules from the module as a whole, not individual
@@ -1442,6 +1447,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
14421447
OI.CompilerOutputType = file_types::TY_Nothing;
14431448
break;
14441449

1450+
case options::OPT_dump_pcm:
1451+
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
1452+
OI.CompilerOutputType = file_types::TY_Nothing;
1453+
break;
1454+
14451455
case options::OPT_i:
14461456
// Keep the default output/mode; this flag was removed and should already
14471457
// have been diagnosed above.
@@ -1850,6 +1860,11 @@ void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
18501860
}
18511861
case OutputInfo::Mode::SingleCompile: {
18521862
if (Inputs.empty()) break;
1863+
if (Args.hasArg(options::OPT_emit_pcm) && Inputs.size() != 1) {
1864+
// -emit-pcm mode requires exactly one input (the module map).
1865+
Diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file);
1866+
return;
1867+
}
18531868
if (Args.hasArg(options::OPT_embed_bitcode)) {
18541869
// Make sure we can handle the inputs.
18551870
bool HandledHere = true;

lib/Driver/ToolChains.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
500500
return "-emit-ir";
501501
case file_types::TY_LLVM_BC:
502502
return "-emit-bc";
503+
case file_types::TY_ClangModuleFile:
504+
return "-emit-pcm";
503505
case file_types::TY_Assembly:
504506
return "-S";
505507
case file_types::TY_SwiftModuleFile:
@@ -523,7 +525,6 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
523525
case file_types::TY_AutolinkFile:
524526
case file_types::TY_Dependencies:
525527
case file_types::TY_SwiftModuleDocFile:
526-
case file_types::TY_ClangModuleFile:
527528
case file_types::TY_SerializedDiagnostics:
528529
case file_types::TY_ObjCHeader:
529530
case file_types::TY_Image:
@@ -759,14 +760,14 @@ ToolChain::constructInvocation(const BackendJobAction &job,
759760
case file_types::TY_SIL:
760761
case file_types::TY_SIB:
761762
case file_types::TY_PCH:
763+
case file_types::TY_ClangModuleFile:
762764
case file_types::TY_IndexData:
763765
llvm_unreachable("Cannot be output from backend job");
764766
case file_types::TY_Swift:
765767
case file_types::TY_dSYM:
766768
case file_types::TY_AutolinkFile:
767769
case file_types::TY_Dependencies:
768770
case file_types::TY_SwiftModuleDocFile:
769-
case file_types::TY_ClangModuleFile:
770771
case file_types::TY_SerializedDiagnostics:
771772
case file_types::TY_ObjCHeader:
772773
case file_types::TY_Image:

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) {
361361
return FrontendOptions::ActionType::DumpTypeInfo;
362362
if (Opt.matches(OPT_print_ast))
363363
return FrontendOptions::ActionType::PrintAST;
364+
if (Opt.matches(OPT_emit_pcm))
365+
return FrontendOptions::ActionType::EmitPCM;
366+
if (Opt.matches(OPT_dump_pcm))
367+
return FrontendOptions::ActionType::DumpPCM;
364368

365369
if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl))
366370
return FrontendOptions::ActionType::REPL;

0 commit comments

Comments
 (0)