Author: Jan Svoboda Date: 2021-07-27T10:47:55+02:00 New Revision: 11ee699b3c812ebe56ce5d3b14ab7ef16c1e8495
URL: https://github.com/llvm/llvm-project/commit/11ee699b3c812ebe56ce5d3b14ab7ef16c1e8495 DIFF: https://github.com/llvm/llvm-project/commit/11ee699b3c812ebe56ce5d3b14ab7ef16c1e8495.diff LOG: [clang][tooling] Accept Clang invocations with multiple jobs When `-fno-integrated-as` is passed to the Clang driver (or set by default by a specific toolchain), it will construct an assembler job in addition to the cc1 job. Similarly, the `-fembed-bitcode` driver flag will create additional cc1 job that reads LLVM IR file. The Clang tooling library only cares about the job that reads a source file. Instead of relying on the fact that the client injected `-fsyntax-only` to the driver invocation to get a single `-cc1` invocation that reads the source file, this patch filters out such jobs from `Compilation` automatically and ignores the rest. This fixes a test failure in `ClangScanDeps/headerwithname.cpp` and `ClangScanDeps/headerwithnamefollowedbyinclude.cpp` on AIX reported here: https://reviews.llvm.org/D103461#2841918 and `clang-scan-deps` failures with `-fembed-bitcode`. Depends on D106788. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D105695 Added: Modified: clang/include/clang/Tooling/Tooling.h clang/lib/Tooling/Tooling.cpp clang/unittests/Tooling/ToolingTest.cpp Removed: clang/test/Tooling/clang-check-offload.cpp ################################################################################ diff --git a/clang/include/clang/Tooling/Tooling.h b/clang/include/clang/Tooling/Tooling.h index 8b3b2e5ad0026..73d09662562b2 100644 --- a/clang/include/clang/Tooling/Tooling.h +++ b/clang/include/clang/Tooling/Tooling.h @@ -66,6 +66,14 @@ namespace tooling { class CompilationDatabase; +/// Retrieves the flags of the `-cc1` job in `Compilation` that has only source +/// files as its inputs. +/// Returns nullptr if there are no such jobs or multiple of them. Note that +/// offloading jobs are ignored. +const llvm::opt::ArgStringList * +getCC1Arguments(DiagnosticsEngine *Diagnostics, + driver::Compilation *Compilation); + /// Interface to process a clang::CompilerInvocation. /// /// If your tool is based on FrontendAction, you should be deriving from diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp index 463f466d9b763..5242134097dac 100644 --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -83,16 +83,20 @@ newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName, return CompilerDriver; } -/// Retrieves the clang CC1 specific flags out of the compilation's jobs. -/// -/// Returns nullptr on error. -static const llvm::opt::ArgStringList *getCC1Arguments( - DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) { - // We expect to get back exactly one Command job, if we didn't something - // failed. Extract that job from the Compilation. +/// Decide whether extra compiler frontend commands can be ignored. +static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) { const driver::JobList &Jobs = Compilation->getJobs(); const driver::ActionList &Actions = Compilation->getActions(); + bool OffloadCompilation = false; + + // Jobs and Actions look very diff erent depending on whether the Clang tool + // injected -fsyntax-only or not. Try to handle both cases here. + + for (const auto &Job : Jobs) + if (StringRef(Job.getExecutable()) == "clang-offload-bundler") + OffloadCompilation = true; + if (Jobs.size() > 1) { for (auto A : Actions){ // On MacOSX real actions may end up being wrapped in BindArchAction @@ -117,8 +121,33 @@ static const llvm::opt::ArgStringList *getCC1Arguments( } } } - if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) || - (Jobs.size() > 1 && !OffloadCompilation)) { + + return OffloadCompilation; +} + +namespace clang { +namespace tooling { + +const llvm::opt::ArgStringList * +getCC1Arguments(DiagnosticsEngine *Diagnostics, + driver::Compilation *Compilation) { + const driver::JobList &Jobs = Compilation->getJobs(); + + auto IsCC1Command = [](const driver::Command &Cmd) { + return StringRef(Cmd.getCreator().getName()) == "clang"; + }; + + auto IsSrcFile = [](const driver::InputInfo &II) { + return isSrcFile(II.getType()); + }; + + llvm::SmallVector<const driver::Command *, 1> CC1Jobs; + for (const driver::Command &Job : Jobs) + if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile)) + CC1Jobs.push_back(&Job); + + if (CC1Jobs.empty() || + (CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) { SmallString<256> error_msg; llvm::raw_svector_ostream error_stream(error_msg); Jobs.Print(error_stream, "; ", true); @@ -127,19 +156,9 @@ static const llvm::opt::ArgStringList *getCC1Arguments( return nullptr; } - // The one job we find should be to invoke clang again. - const auto &Cmd = cast<driver::Command>(*Jobs.begin()); - if (StringRef(Cmd.getCreator().getName()) != "clang") { - Diagnostics->Report(diag::err_fe_expected_clang_command); - return nullptr; - } - - return &Cmd.getArguments(); + return &CC1Jobs[0]->getArguments(); } -namespace clang { -namespace tooling { - /// Returns a clang build invocation initialized from the CC1 flags. CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics, const llvm::opt::ArgStringList &CC1Args, diff --git a/clang/test/Tooling/clang-check-offload.cpp b/clang/test/Tooling/clang-check-offload.cpp deleted file mode 100644 index 154bc043113e4..0000000000000 --- a/clang/test/Tooling/clang-check-offload.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: not clang-check "%s" -- -c -x hip -nogpulib 2>&1 | FileCheck %s - -// CHECK: C++ requires -invalid; diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp index 313e8325c615b..fe85304860069 100644 --- a/clang/unittests/Tooling/ToolingTest.cpp +++ b/clang/unittests/Tooling/ToolingTest.cpp @@ -9,6 +9,8 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -18,6 +20,7 @@ #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" @@ -258,6 +261,105 @@ TEST(ToolInvocation, DiagConsumerExpectingSourceManager) { EXPECT_TRUE(Consumer.SawSourceManager); } +namespace { +/// Overlays the real filesystem with the given VFS and returns the result. +llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> +overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + auto RFS = llvm::vfs::getRealFileSystem(); + auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS); + OverlayFS->pushOverlay(VFS); + return OverlayFS; +} + +struct CommandLineExtractorTest : public ::testing::Test { + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS; + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags; + driver::Driver Driver; + +public: + CommandLineExtractorTest() + : InMemoryFS(new llvm::vfs::InMemoryFileSystem), + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)), + Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags, + "clang LLVM compiler", overlayRealFS(InMemoryFS)) {} + + void addFile(StringRef Name, StringRef Content) { + InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content)); + } + + const llvm::opt::ArgStringList * + extractCC1Arguments(llvm::ArrayRef<const char *> Argv) { + const std::unique_ptr<driver::Compilation> Compilation( + Driver.BuildCompilation(llvm::makeArrayRef(Argv))); + + return getCC1Arguments(Diags.get(), Compilation.get()); + } +}; +} // namespace + +TEST_F(CommandLineExtractorTest, AcceptOffloading) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-x", "hip", "test.c", + "-nogpulib", "-nogpuinc"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-c", "-x", "hip", + "test.c", "-nogpulib", "-nogpuinc"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = { + "clang", "-target", "arm64-apple-macosx11.0.0", + "-fsyntax-only", "-x", "hip", + "test.c", "-nogpulib", "-nogpuinc"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = { + "clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as", + "-c", "test.c"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-c", "-fembed-bitcode", "test.c"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, AcceptSaveTemps) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-c", "-save-temps", "test.c"}; + EXPECT_NE(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) { + addFile("test.c", "int main() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-arch", "x86_64", "-arch", + "arm64", "-c", "test.c"}; + EXPECT_EQ(extractCC1Arguments(Args), nullptr); +} + +TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) { + addFile("one.c", "void one() {}\n"); + addFile("two.c", "void two() {}\n"); + const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", + "-c", "one.c", "two.c"}; + EXPECT_EQ(extractCC1Arguments(Args), nullptr); +} + struct VerifyEndCallback : public SourceFileCallbacks { VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} bool handleBeginSource(CompilerInstance &CI) override { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits