Skip to content

Add driver flag to precompile Swift-compatible explicit Clang modules. #28107

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 2 commits into from
Nov 14, 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
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ ERROR(bridging_header_pch_error,Fatal,
"failed to emit precompiled header '%0' for bridging header '%1'",
(StringRef, StringRef))

ERROR(emit_pcm_error,Fatal,
"failed to emit precompiled module '%0' for module map '%1'",
(StringRef, StringRef))
ERROR(dump_pcm_error,Fatal,
"failed to dump precompiled module '%0'", (StringRef))

WARNING(invalid_swift_name_method,none,
"too %select{few|many}0 parameters in swift_name attribute (expected %1; "
"got %2)", (bool, unsigned, unsigned))
Expand Down
24 changes: 24 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ class ClangImporter final : public ClangModuleLoader {
DependencyTracker *tracker,
DWARFImporterDelegate *dwarfImporterDelegate);

/// Creates a clone of Clang importer's compiler instance that has been
/// configured for operations on precompiled outputs (either emitting a
/// precompiled header, emitting a precompiled module, or dumping a
/// precompiled module).
///
/// The caller of this method should set any action-specific invocation
/// options (like FrontendOptions::ProgramAction, input files, and output
/// paths), then create the appropriate FrontendAction and execute it.
std::unique_ptr<clang::CompilerInstance>
cloneCompilerInstanceForPrecompiling();

public:
/// Create a new Clang importer that can import a suitable Clang
/// module into the given ASTContext.
Expand Down Expand Up @@ -333,6 +344,19 @@ class ClangImporter final : public ClangModuleLoader {
/// if we need to persist a PCH for later reuse.
bool canReadPCH(StringRef PCHFilename);

/// Makes a temporary replica of the ClangImporter's CompilerInstance, reads a
/// module map into the replica and emits a PCM file for one of the modules it
/// declares. Delegates to clang for everything except construction of the
/// replica.
bool emitPrecompiledModule(StringRef moduleMapPath, StringRef moduleName,
StringRef outputPath);

/// Makes a temporary replica of the ClangImporter's CompilerInstance and
/// dumps information about a PCM file (assumed to be generated by -emit-pcm
/// or in the Swift module cache). Delegates to clang for everything except
/// construction of the replica.
bool dumpPrecompiledModule(StringRef modulePath, StringRef outputPath);

const clang::Module *getClangOwningModule(ClangNode Node) const;
bool hasTypedef(const clang::Decl *typeDecl) const;

Expand Down
5 changes: 4 additions & 1 deletion include/swift/ClangImporter/ClangImporterOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ class ClangImporterOptions {
/// Swift code.
Normal,
/// Set up Clang for backend compilation only.
EmbedBitcode
EmbedBitcode,
/// Set up Clang to emit a precompiled module from a C/Objective-C module
/// map or dump debugging info about a precompiled module.
PrecompiledModule
};

/// Controls how Clang is initially set up.
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ class CompilerInvocation {

void setRuntimeResourcePath(StringRef Path);

/// Computes the runtime resource path relative to the given Swift
/// executable.
static void computeRuntimeResourcePathFromExecutablePath(
StringRef mainExecutablePath,
llvm::SmallString<128> &runtimeResourcePath);

void setSDKPath(const std::string &Path);

StringRef getSDKPath() const {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class FrontendOptions {
EmitObject, ///< Emit object file

DumpTypeInfo, ///< Dump IRGen type info

EmitPCM, ///< Emit precompiled Clang module from a module map
DumpPCM, ///< Dump information about a precompiled Clang module
};

/// Indicates the action the user requested that the frontend perform.
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,9 @@ def emit_sibgen : Flag<["-"], "emit-sibgen">,
def emit_imported_modules : Flag<["-"], "emit-imported-modules">,
HelpText<"Emit a list of the imported modules">, ModeOpt,
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
def emit_pcm : Flag<["-"], "emit-pcm">,
HelpText<"Emit a precompiled Clang module from a module map">, ModeOpt,
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;

def c : Flag<["-"], "c">, Alias<emit_object>,
Flags<[FrontendOption, NoInteractiveOption]>, ModeOpt;
Expand Down Expand Up @@ -792,6 +795,10 @@ def print_ast : Flag<["-"], "print-ast">,
HelpText<"Parse and type-check input file(s) and pretty print AST(s)">,
ModeOpt,
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
def dump_pcm : Flag<["-"], "dump-pcm">,
HelpText<"Dump debugging information about a precompiled Clang module">,
ModeOpt,
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;

// Other Modes
def repl : Flag<["-"], "repl">,
Expand Down
165 changes: 137 additions & 28 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts,
invocationArgStrs.push_back("clang");
switch (importerOpts.Mode) {
case ClangImporterOptions::Modes::Normal:
case ClangImporterOptions::Modes::PrecompiledModule:
getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts);
break;
case ClangImporterOptions::Modes::EmbedBitcode:
Expand Down Expand Up @@ -1062,7 +1063,17 @@ ClangImporter::create(ASTContext &ctx, const ClangImporterOptions &importerOpts,
if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode)
return importer;

// ClangImporter always sets this in Normal mode, so we need to make sure to
// set it before bailing out early when configuring ClangImporter for
// precompiled modules. This is not a benign langopt, so forgetting this (for
// example, if we combined the early exit below with the one above) would make
// the compiler instance used to emit PCMs incompatible with the one used to
// read them later.
instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true;

if (importerOpts.Mode == ClangImporterOptions::Modes::PrecompiledModule)
return importer;

bool canBegin = action->BeginSourceFile(instance,
instance.getFrontendOpts().Inputs[0]);
if (!canBegin)
Expand Down Expand Up @@ -1446,43 +1457,82 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
return result;
}

bool
ClangImporter::emitBridgingPCH(StringRef headerPath,
StringRef outputPCHPath) {
/// Returns the appropriate source input language based on language options.
static clang::Language getLanguageFromOptions(
const clang::LangOptions *LangOpts) {
if (LangOpts->OpenCL)
return clang::Language::OpenCL;
if (LangOpts->CUDA)
return clang::Language::CUDA;
if (LangOpts->ObjC)
return LangOpts->CPlusPlus ?
clang::Language::ObjCXX : clang::Language::ObjC;
return LangOpts->CPlusPlus ? clang::Language::CXX : clang::Language::C;
}

/// Wraps the given frontend action in an index data recording action if the
/// frontend options have an index store path specified.
static
std::unique_ptr<clang::FrontendAction> wrapActionForIndexingIfEnabled(
const clang::FrontendOptions &FrontendOpts,
std::unique_ptr<clang::FrontendAction> action) {
if (!FrontendOpts.IndexStorePath.empty()) {
return clang::index::createIndexDataRecordingAction(
FrontendOpts, std::move(action));
}
return action;
}

std::unique_ptr<clang::CompilerInstance>
ClangImporter::cloneCompilerInstanceForPrecompiling() {
auto invocation =
std::make_shared<clang::CompilerInvocation>(*Impl.Invocation);
invocation->getFrontendOpts().DisableFree = false;
invocation->getFrontendOpts().Inputs.clear();
invocation->getFrontendOpts().Inputs.push_back(
clang::FrontendInputFile(headerPath, clang::Language::ObjC));
invocation->getFrontendOpts().OutputFile = outputPCHPath;
invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH;
invocation->getPreprocessorOpts().resetNonModularOptions();
invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true;
invocation->getLangOpts()->CacheGeneratedPCH = true;

clang::CompilerInstance emitInstance(
auto &PPOpts = invocation->getPreprocessorOpts();
PPOpts.resetNonModularOptions();

auto &FrontendOpts = invocation->getFrontendOpts();
FrontendOpts.DisableFree = false;
FrontendOpts.Inputs.clear();

auto clonedInstance = llvm::make_unique<clang::CompilerInstance>(
Impl.Instance->getPCHContainerOperations(),
&Impl.Instance->getModuleCache());
emitInstance.setInvocation(std::move(invocation));
emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(),
/*ShouldOwnClient=*/false);
clonedInstance->setInvocation(std::move(invocation));
clonedInstance->createDiagnostics(&Impl.Instance->getDiagnosticClient(),
/*ShouldOwnClient=*/false);

clang::FileManager &fileManager = Impl.Instance->getFileManager();
emitInstance.setFileManager(&fileManager);
emitInstance.createSourceManager(fileManager);
emitInstance.setTarget(&Impl.Instance->getTarget());
clonedInstance->setFileManager(&fileManager);
clonedInstance->createSourceManager(fileManager);
clonedInstance->setTarget(&Impl.Instance->getTarget());

std::unique_ptr<clang::FrontendAction> action;
action.reset(new clang::GeneratePCHAction());
if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) {
action = clang::index::
createIndexDataRecordingAction(emitInstance.getFrontendOpts(),
std::move(action));
}
emitInstance.ExecuteAction(*action);
return clonedInstance;
}

bool
ClangImporter::emitBridgingPCH(StringRef headerPath,
StringRef outputPCHPath) {
auto emitInstance = cloneCompilerInstanceForPrecompiling();
auto &invocation = emitInstance->getInvocation();

auto LangOpts = invocation.getLangOpts();
LangOpts->NeededByPCHOrCompilationUsesPCH = true;
LangOpts->CacheGeneratedPCH = true;

auto language = getLanguageFromOptions(LangOpts);
auto inputFile = clang::FrontendInputFile(headerPath, language);

auto &FrontendOpts = invocation.getFrontendOpts();
FrontendOpts.Inputs = {inputFile};
FrontendOpts.OutputFile = outputPCHPath;
FrontendOpts.ProgramAction = clang::frontend::GeneratePCH;

if (emitInstance.getDiagnostics().hasErrorOccurred()) {
auto action = wrapActionForIndexingIfEnabled(
FrontendOpts, llvm::make_unique<clang::GeneratePCHAction>());
emitInstance->ExecuteAction(*action);

if (emitInstance->getDiagnostics().hasErrorOccurred()) {
Impl.SwiftContext.Diags.diagnose({},
diag::bridging_header_pch_error,
outputPCHPath, headerPath);
Expand All @@ -1491,6 +1541,65 @@ ClangImporter::emitBridgingPCH(StringRef headerPath,
return false;
}

bool ClangImporter::emitPrecompiledModule(StringRef moduleMapPath,
StringRef moduleName,
StringRef outputPath) {
auto emitInstance = cloneCompilerInstanceForPrecompiling();
auto &invocation = emitInstance->getInvocation();

auto LangOpts = invocation.getLangOpts();
LangOpts->setCompilingModule(clang::LangOptions::CMK_ModuleMap);
LangOpts->ModuleName = moduleName;
LangOpts->CurrentModule = LangOpts->ModuleName;

auto language = getLanguageFromOptions(LangOpts);
auto inputFile = clang::FrontendInputFile(
moduleMapPath, clang::InputKind(
language, clang::InputKind::ModuleMap, false));

auto &FrontendOpts = invocation.getFrontendOpts();
FrontendOpts.Inputs = {inputFile};
FrontendOpts.OriginalModuleMap = moduleMapPath;
FrontendOpts.OutputFile = outputPath;
FrontendOpts.ProgramAction = clang::frontend::GenerateModule;

auto action = wrapActionForIndexingIfEnabled(
FrontendOpts,
llvm::make_unique<clang::GenerateModuleFromModuleMapAction>());
emitInstance->ExecuteAction(*action);

if (emitInstance->getDiagnostics().hasErrorOccurred()) {
Impl.SwiftContext.Diags.diagnose({},
diag::emit_pcm_error,
outputPath, moduleMapPath);
return true;
}
return false;
}

bool ClangImporter::dumpPrecompiledModule(StringRef modulePath,
StringRef outputPath) {
auto dumpInstance = cloneCompilerInstanceForPrecompiling();
auto &invocation = dumpInstance->getInvocation();

auto inputFile = clang::FrontendInputFile(
modulePath, clang::InputKind(
clang::Language::Unknown, clang::InputKind::Precompiled, false));

auto &FrontendOpts = invocation.getFrontendOpts();
FrontendOpts.Inputs = {inputFile};
FrontendOpts.OutputFile = outputPath;

auto action = llvm::make_unique<clang::DumpModuleInfoAction>();
dumpInstance->ExecuteAction(*action);

if (dumpInstance->getDiagnostics().hasErrorOccurred()) {
Impl.SwiftContext.Diags.diagnose({}, diag::dump_pcm_error, modulePath);
return true;
}
return false;
}

void ClangImporter::collectVisibleTopLevelModuleNames(
SmallVectorImpl<Identifier> &names) const {
SmallVector<clang::Module *, 32> Modules;
Expand Down
15 changes: 15 additions & 0 deletions lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
OI.CompilerOutputType = file_types::TY_PCH;
break;

case options::OPT_emit_pcm:
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
OI.CompilerOutputType = file_types::TY_ClangModuleFile;
break;

case options::OPT_emit_imported_modules:
OI.CompilerOutputType = file_types::TY_ImportedModules;
// We want the imported modules from the module as a whole, not individual
Expand Down Expand Up @@ -1442,6 +1447,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
OI.CompilerOutputType = file_types::TY_Nothing;
break;

case options::OPT_dump_pcm:
OI.CompilerMode = OutputInfo::Mode::SingleCompile;
OI.CompilerOutputType = file_types::TY_Nothing;
break;

case options::OPT_i:
// Keep the default output/mode; this flag was removed and should already
// have been diagnosed above.
Expand Down Expand Up @@ -1842,6 +1852,11 @@ void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
}
case OutputInfo::Mode::SingleCompile: {
if (Inputs.empty()) break;
if (Args.hasArg(options::OPT_emit_pcm) && Inputs.size() != 1) {
// -emit-pcm mode requires exactly one input (the module map).
Diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file);
return;
}
if (Args.hasArg(options::OPT_embed_bitcode)) {
// Make sure we can handle the inputs.
bool HandledHere = true;
Expand Down
5 changes: 3 additions & 2 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
return "-emit-ir";
case file_types::TY_LLVM_BC:
return "-emit-bc";
case file_types::TY_ClangModuleFile:
return "-emit-pcm";
case file_types::TY_Assembly:
return "-S";
case file_types::TY_SwiftModuleFile:
Expand All @@ -522,7 +524,6 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
case file_types::TY_AutolinkFile:
case file_types::TY_Dependencies:
case file_types::TY_SwiftModuleDocFile:
case file_types::TY_ClangModuleFile:
case file_types::TY_SerializedDiagnostics:
case file_types::TY_ObjCHeader:
case file_types::TY_Image:
Expand Down Expand Up @@ -758,14 +759,14 @@ ToolChain::constructInvocation(const BackendJobAction &job,
case file_types::TY_SIL:
case file_types::TY_SIB:
case file_types::TY_PCH:
case file_types::TY_ClangModuleFile:
case file_types::TY_IndexData:
llvm_unreachable("Cannot be output from backend job");
case file_types::TY_Swift:
case file_types::TY_dSYM:
case file_types::TY_AutolinkFile:
case file_types::TY_Dependencies:
case file_types::TY_SwiftModuleDocFile:
case file_types::TY_ClangModuleFile:
case file_types::TY_SerializedDiagnostics:
case file_types::TY_ObjCHeader:
case file_types::TY_Image:
Expand Down
4 changes: 4 additions & 0 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) {
return FrontendOptions::ActionType::DumpTypeInfo;
if (Opt.matches(OPT_print_ast))
return FrontendOptions::ActionType::PrintAST;
if (Opt.matches(OPT_emit_pcm))
return FrontendOptions::ActionType::EmitPCM;
if (Opt.matches(OPT_dump_pcm))
return FrontendOptions::ActionType::DumpPCM;

if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl))
return FrontendOptions::ActionType::REPL;
Expand Down
Loading