Skip to content

[ClangImporter] Add a mode to create the underlying Clang 'CompilerInvocation' directly from 'cc1' arguments, bypassing the Clang driver, only in the 'emit-pcm' compilation flow. #61303

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 1 commit into from
Sep 27, 2022
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
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ WARNING(warning_cannot_multithread_batch_mode,none,
"ignoring -num-threads argument; cannot multithread batch mode", ())
ERROR(error_cannot_explicit_interface_build_in_mode,none,
"'-explicit-interface-module-build' only supported when building a module from interface ('-compile-module-from-interface')'", ())
ERROR(error_cannot_direct_cc1_pcm_build_in_mode,none,
"'-direct-clang-cc1-module-build' only supported when building a PCM ('-emit-pcm')'", ())
ERROR(error_unsupported_option_argument,none,
"unsupported argument '%1' to option '%0'", (StringRef, StringRef))
ERROR(error_immediate_mode_missing_stdlib,none,
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,11 @@ namespace swift {
/// contains the full option set.
bool ExtraArgsOnly = false;

/// When building a PCM, rely on the Swift frontend's command-line -Xcc flags
/// to build the Clang module via Clang frontend directly,
/// and completly bypass the Clang driver.
bool DirectClangCC1ModuleBuild = false;

/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@ def explicit_interface_module_build :
Flag<["-"], "explicit-interface-module-build">,
HelpText<"Use the specified command-line to build the module from interface, instead of flags specified in the interface">;

def direct_clang_cc1_module_build :
Flag<["-"], "direct-clang-cc1-module-build">,
HelpText<"Use the specified -Xcc options to build a PCM by using Clang frontend directly, bypassing the Clang driver">;

def build_module_from_parseable_interface :
Flag<["-"], "build-module-from-parseable-interface">,
Alias<compile_module_from_interface>,
Expand Down
71 changes: 47 additions & 24 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,27 +993,51 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
invocationArgs.reserve(invocationArgStrs.size());
for (auto &argStr : invocationArgStrs)
invocationArgs.push_back(argStr.c_str());
// Set up a temporary diagnostic client to report errors from parsing the
// command line, which may be important for Swift clients if, for example,
// they're using -Xcc options. Unfortunately this diagnostic engine has to
// use the default options because the /actual/ options haven't been parsed
// yet.
//
// The long-term client for Clang diagnostics is set up below, after the
// clang::CompilerInstance is created.
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
new clang::DiagnosticOptions
};

ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts,
importerOpts.DumpClangDiagnostics};
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> tempClangDiags =
clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(),
&tempDiagClient,
/*owned*/false);
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> clangDiags;
std::unique_ptr<clang::CompilerInvocation> CI;
if (importerOpts.DirectClangCC1ModuleBuild) {
// In this mode, we bypass createInvocationFromCommandLine, which goes
// through the Clang driver, and use strictly cc1 arguments to instantiate a
// clang Instance directly, assuming that the set of '-Xcc <X>' frontend flags is
// fully sufficient to do so.

// Because we are bypassing the Clang driver, we must populate
// the diagnostic options here explicitly.
std::unique_ptr<clang::DiagnosticOptions> clangDiagOpts =
clang::CreateAndPopulateDiagOpts(invocationArgs);
ClangDiagnosticConsumer diagClient{importer->Impl, *clangDiagOpts,
importerOpts.DumpClangDiagnostics};
clangDiags = clang::CompilerInstance::createDiagnostics(
clangDiagOpts.release(), &diagClient,
/*owned*/ false);

// Finally, use the CC1 command-line and the diagnostic engine
// to instantiate our Invocation.
CI = std::make_unique<clang::CompilerInvocation>();
if (!clang::CompilerInvocation::CreateFromArgs(
*CI, invocationArgs, *clangDiags, invocationArgs[0]))
return nullptr;
} else {
// Set up a temporary diagnostic client to report errors from parsing the
// command line, which may be important for Swift clients if, for example,
// they're using -Xcc options. Unfortunately this diagnostic engine has to
// use the default options because the /actual/ options haven't been parsed
// yet.
//
// The long-term client for Clang diagnostics is set up below, after the
// clang::CompilerInstance is created.
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
new clang::DiagnosticOptions};

auto CI = clang::createInvocationFromCommandLine(
invocationArgs, tempClangDiags, VFS, false, CC1Args);
ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts,
importerOpts.DumpClangDiagnostics};
clangDiags = clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(),
&tempDiagClient,
/*owned*/ false);
CI = clang::createInvocationFromCommandLine(invocationArgs, clangDiags, VFS,
false, CC1Args);
}

if (!CI) {
return CI;
Expand All @@ -1030,8 +1054,9 @@ std::unique_ptr<clang::CompilerInvocation> ClangImporter::createClangInvocation(
// rdar://77516546 is tracking that the clang importer should be more
// resilient and provide a module even if there were building it.
auto TempVFS = clang::createVFSFromCompilerInvocation(
*CI, *tempClangDiags,
*CI, *clangDiags,
VFS ? VFS : importer->Impl.SwiftContext.SourceMgr.getFileSystem());

std::vector<std::string> FilteredModuleMapFiles;
for (auto ModuleMapFile : CI->getFrontendOpts().ModuleMapFiles) {
if (TempVFS->exists(ModuleMapFile)) {
Expand All @@ -1058,13 +1083,11 @@ ClangImporter::create(ASTContext &ctx,
if (importerOpts.DumpClangDiagnostics) {
llvm::errs() << "'";
llvm::interleave(
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
[] { llvm::errs() << "' '"; });
invocationArgStrs, [](StringRef arg) { llvm::errs() << arg; },
[] { llvm::errs() << "' '"; });
llvm::errs() << "'\n";
}



if (isPCHFilenameExtension(importerOpts.BridgingHeader)) {
importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader);
importer->Impl.IsReadingBridgingPCH = true;
Expand Down
1 change: 1 addition & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts,
}

Opts.ExtraArgsOnly |= Args.hasArg(OPT_extra_clang_options_only);
Opts.DirectClangCC1ModuleBuild |= Args.hasArg(OPT_direct_clang_cc1_module_build);

if (const Arg *A = Args.getLastArg(OPT_pch_output_dir)) {
Opts.PrecompiledHeaderOutputDir = A->getValue();
Expand Down
16 changes: 16 additions & 0 deletions test/ClangImporter/pcm-emit-direct-cc1-mode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Emit the explicit module.
// RUN: %empty-directory(%t)
// RUN: %swift-frontend -emit-pcm -direct-clang-cc1-module-build -only-use-extra-clang-opts -module-name script -o %t/script.pcm %S/Inputs/custom-modules/module.map -Xcc %S/Inputs/custom-modules/module.map -Xcc -o -Xcc %t/script.pcm -Xcc -fmodules -Xcc -triple -Xcc %target-triple -Xcc -x -Xcc objective-c -dump-clang-diagnostics 2> %t.diags.txt

// Verify some of the output of the -dump-pcm flag.
// RUN: %swift-dump-pcm %t/script.pcm | %FileCheck %s --check-prefix=CHECK-DUMP
// CHECK-DUMP: Information for module file '{{.*}}/script.pcm':
// CHECK-DUMP: Module name: script
// CHECK-DUMP: Module map file: {{.*[/\\]}}Inputs{{/|\\}}custom-modules{{/|\\}}module.map

// Verify that the clang command-line used is cc1
// RUN: %FileCheck -check-prefix CHECK-CLANG %s < %t.diags.txt
// CHECK-CLANG: '{{.*[/\\]}}clang'{{.*}}'-fmodules'

import script
var _ : ScriptTy