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

Reply via email to