paulkirth created this revision. paulkirth added reviewers: phosek, leonardchan, nickdesaulniers. Herald added subscribers: llvm-commits, cfe-commits, hiraditya, mgorny. Herald added projects: clang, LLVM.
This is a continuation of the work in D66324 <https://reviews.llvm.org/D66324> that creates a standalone tool that can simply be run over an entire codebase through the use of a compile commands database. This simplifies the need to support custom build targets to check a project's use of `__builtin_expect()`. The design is fairly straightforward: use a libTooling executor to walk the compilation database, and run the compiler over each target after curating some compiler flags. Mainly, we remove any incompatible flags, and then configure the codegen options to support using the selected type of profile data. We also process the LLVMArgs, as libTooling executors seem to bypass the normal mechanisms for passing flags to the LLVM backend. Other than these few changes we rely on the existing compiler infrastructure to generate our diagnostics. Currently the use of the `CodeGenAction` with the libTooling Executor seems to be surfacing some race conditions within the compiler infrastructure. For now I plan to limit the concurrency of the executor to run single threaded, but eventually, I would like to have this run with full parallelism. If a `CodeGenAction` can never be safely run in parallel, then it may become necessary to use an external driver script, similar to `run_clang_tidy.py` to take advantage of parallelism. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D67253 Files: clang-tools-extra/CMakeLists.txt clang-tools-extra/clang-misexpect/CMakeLists.txt clang-tools-extra/clang-misexpect/ClangMisExpect.cpp clang-tools-extra/clang-misexpect/ClangMisExpect.h clang-tools-extra/clang-misexpect/tool/CMakeLists.txt clang-tools-extra/clang-misexpect/tool/ClangMisExpectMain.cpp clang/cmake/caches/Fuchsia-stage2.cmake llvm/lib/Transforms/Utils/MisExpect.cpp
Index: llvm/lib/Transforms/Utils/MisExpect.cpp =================================================================== --- llvm/lib/Transforms/Utils/MisExpect.cpp +++ llvm/lib/Transforms/Utils/MisExpect.cpp @@ -43,7 +43,8 @@ static cl::opt<bool> PGOWarnMisExpect( "pgo-warn-misexpect", cl::init(false), cl::Hidden, cl::desc("Use this option to turn on/off " - "warnings about incorrect usage of llvm.expect intrinsics.")); + "warnings about incorrect usage of llvm.expect intrinsics."), + cl::ZeroOrMore); } // namespace llvm Index: clang/cmake/caches/Fuchsia-stage2.cmake =================================================================== --- clang/cmake/caches/Fuchsia-stage2.cmake +++ clang/cmake/caches/Fuchsia-stage2.cmake @@ -207,6 +207,7 @@ LTO clang-apply-replacements clang-doc + clang-misexpect clang-format clang-resource-headers clang-include-fixer Index: clang-tools-extra/clang-misexpect/tool/ClangMisExpectMain.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-misexpect/tool/ClangMisExpectMain.cpp @@ -0,0 +1,124 @@ +//===-- ClangMisExpectMain.cpp - ClangMisexpect -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the main function for clang misexpect. It uses a +// libTooling exectutor to check each file in the compiler_commands.json against +// a provided PGO profile. When profile counters disagree with the compiler's +// threshold values for likely and unlike branches clang-misexpect will issue a +// diagnostic message. +// +//===----------------------------------------------------------------------===// + +#include "../ClangMisExpect.h" +#include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/AllTUsExecution.h" +#include "clang/Tooling/ArgumentsAdjusters.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Execution.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include <string> + +using namespace clang; +using namespace clang::tooling; +using namespace clang::misexpect; +using Path = std::string; + +static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static llvm::cl::OptionCategory + ClangMisExpectCategory("clang-misexpect options"); + +static llvm::cl::opt<Path> ProfileDir( + "profile-dir", + llvm::cl::desc( + "Specify a path to the profile data to use during validation"), + llvm::cl::cat(ClangMisExpectCategory)); + +static llvm::cl::opt<ProfileKind> ProfFormat( + "profile-format", + llvm::cl::desc( + "Specify the format of the profile data used during validation"), + llvm::cl::init(Clang), + llvm::cl::values(clEnumValN(Clang, "clang", "Clang Instrumentation"), + clEnumValN(IR, "llvm", "IR Instrumentation"), + clEnumValN(CSIR, "csllvm", + "Context sensitive IR Instrumentation"), + clEnumValN(Sample, "sample", "Sampling Instrumentation")), + llvm::cl::cat(ClangMisExpectCategory)); + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + + ExecutorName.setInitialValue("all-TUs"); + // ExecutorName.setInitialValue("standalone"); + + auto Executor = + createExecutorFromCommandLineArgs(argc, argv, ClangMisExpectCategory); + + if (!Executor) { + llvm::errs() << "Failed to create executor --- " + << llvm::toString(Executor.takeError()) << "\n"; + return 1; + } + + llvm::errs() << "Executor Created ... \n"; + + CommonOptionsParser OptionsParser(argc, argv, ClangMisExpectCategory, + llvm::cl::ZeroOrMore); + + auto &OS = llvm::errs(); + OS << "Starting execution ... \n"; + auto ArgAdjuster = getStripPluginsAdjuster(); + auto StripProfileWarnings = [](const CommandLineArguments &Args, + StringRef /*unused*/ Unused) { + CommandLineArguments AdjustedArgs; + std::set<std::string> FilteredArgs = {"-Wprofile-instr-unprofiled", + "-fcoverage-mapping", "-Werror"}; + for (size_t I = 0, E = Args.size(); I != E; I++) { + if (FilteredArgs.find(Args[I]) != FilteredArgs.end()) { + continue; + } + AdjustedArgs.push_back(Args[I]); + } + return AdjustedArgs; + }; + + ArgAdjuster = combineAdjusters(StripProfileWarnings, ArgAdjuster); + + ArgAdjuster = + combineAdjusters(getInsertArgumentAdjuster( + { + "-Wmisexpect", "-Wno-profile-instr-unprofiled", + "-Wno-profile-instr-out-of-date" + }, + tooling::ArgumentInsertPosition::END), + ArgAdjuster); + + auto Err = Executor->get()->execute( + std::make_unique<misexpect::MisExpectFactory>(ProfileDir, ProfFormat), + ArgAdjuster); + if (Err) { + OS.changeColor(raw_ostream::Colors::RED, true); + OS << "Error: "; + OS.resetColor(); + OS << llvm::toString(std::move(Err)) << "\n"; + } + + llvm::errs() << "Execution complete\n"; + + // Emit collected data. + Executor->get()->getToolResults()->forEachResult( + [](llvm::StringRef Key, llvm::StringRef Value) { + llvm::errs() << "----" << Key.str() << "\n" << Value.str() << "\n"; + }); + return 0; +} Index: clang-tools-extra/clang-misexpect/tool/CMakeLists.txt =================================================================== --- /dev/null +++ clang-tools-extra/clang-misexpect/tool/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsInfos + support + ) + +add_clang_tool(clang-misexpect + ClangMisExpectMain.cpp + ) +add_dependencies(clang-misexpect + clang-resource-headers + ) +target_link_libraries(clang-misexpect + PRIVATE + clangBasic + clangMisExpect + clangFrontend + clangCodeGen + clangTooling + clangToolingCore + clangToolingSyntax + ) + Index: clang-tools-extra/clang-misexpect/ClangMisExpect.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-misexpect/ClangMisExpect.h @@ -0,0 +1,55 @@ +//===-- ClangMisExpect.h - ClangMisexpect -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a method to create the FrontendActionFactory for the +// clang-misexpect tool. The factory consumes a compilation database and valid +// profiling data to run the compiler over a codebase and issue warnings +// generated from the -Wmisexpect compiler flags. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/StringRef.h" +#include <string> + +namespace clang { +namespace misexpect { + +enum ProfileKind { + Clang, + IR, + CSIR, + Sample, +}; + +class MisExpectFactory : public tooling::FrontendActionFactory { + using Path = std::string; + +public: + MisExpectFactory(Path Profile, ProfileKind ProfileType); + + bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, + FileManager *Files, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + DiagnosticConsumer *DiagConsumer) override; + + std::unique_ptr<FrontendAction> create() override; + +private: + Path ProfilePath; + ProfileKind ProfileType; +}; + +} // namespace misexpect +} // namespace clang Index: clang-tools-extra/clang-misexpect/ClangMisExpect.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-misexpect/ClangMisExpect.cpp @@ -0,0 +1,88 @@ +//===-- ClangMisExpect.cpp - ClangMisexpect ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a method to create the FrontendActionFactory for the +// clang-misexpect tool. The factory consumes a compilation database and valid +// profiling data to run the compiler over a codebase and issue warnings +// generated from the -Wmisexpect compiler flags. +// +//===----------------------------------------------------------------------===// + +#include "ClangMisExpect.h" +#include "clang/Basic/CodeGenOptions.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::tooling; +using namespace misexpect; + +#define DEBUG_TYPE "misexpect" + +MisExpectFactory::MisExpectFactory(Path ProfilePath, ProfileKind ProfileType) + : ProfilePath(ProfilePath), ProfileType(ProfileType) {} + +std::unique_ptr<FrontendAction> MisExpectFactory::create() { + return std::make_unique<EmitLLVMOnlyAction>(); +} + +bool MisExpectFactory::runInvocation( + std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + DiagnosticConsumer *DiagConsumer) { + // Only run the compiler through IR generation + Invocation->getFrontendOpts().ProgramAction = frontend::EmitLLVMOnly; + + // clear the existing profile flags and metadata + Invocation->getCodeGenOpts().setProfileUse(CodeGenOptions::ProfileNone); + Invocation->getCodeGenOpts().setProfileInstr(CodeGenOptions::ProfileNone); + Invocation->getCodeGenOpts().ProfileInstrumentUsePath = ""; + Invocation->getCodeGenOpts().SampleProfileFile = ""; + + // duplicate the logic in ExecuteCompilerInvocation to process llvm options + if (!Invocation->getFrontendOpts().LLVMArgs.empty()) { + unsigned NumArgs = Invocation->getFrontendOpts().LLVMArgs.size(); + auto Args = std::make_unique<const char *[]>(NumArgs + 2); + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Invocation->getFrontendOpts().LLVMArgs[i].c_str(); + Args[NumArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); + } + + // set new profiling options based on profile type + switch (ProfileType) { + case ProfileKind::Clang: + Invocation->getCodeGenOpts().setProfileUse( + CodeGenOptions::ProfileClangInstr); + break; + case ProfileKind::IR: + Invocation->getCodeGenOpts().setProfileUse(CodeGenOptions::ProfileIRInstr); + break; + case ProfileKind::CSIR: + Invocation->getCodeGenOpts().setProfileUse( + CodeGenOptions::ProfileCSIRInstr); + break; + case ProfileKind::Sample: + Invocation->getCodeGenOpts().SampleProfileFile = ProfilePath; + break; + llvm_unreachable("Bad Profile Format given to clang-misexpect use one of " + "(clang, llvm, csllvm, sample)"); + }; + + if (ProfileType != ProfileKind::Sample) + Invocation->getCodeGenOpts().ProfileInstrumentUsePath = ProfilePath; + + return FrontendActionFactory::runInvocation(Invocation, Files, + PCHContainerOps, DiagConsumer); +} + +#undef DEBUG_TYPE Index: clang-tools-extra/clang-misexpect/CMakeLists.txt =================================================================== --- /dev/null +++ clang-tools-extra/clang-misexpect/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangMisExpect + ClangMisExpect.cpp + + LINK_LIBS + clangBasic + clangCodeGen + clangFrontend + clangFrontendTool + clangTooling + clangToolingCore + ) + +add_subdirectory(tool) Index: clang-tools-extra/CMakeLists.txt =================================================================== --- clang-tools-extra/CMakeLists.txt +++ clang-tools-extra/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) add_subdirectory(clang-tidy) +add_subdirectory(clang-misexpect) add_subdirectory(clang-change-namespace) add_subdirectory(clang-doc)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits