[llvm] r318356 - [globalisel][tablegen] Generate rule coverage and use it to identify untested rules

Daniel Sanders via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 15 16:46:35 PST 2017


Author: dsanders
Date: Wed Nov 15 16:46:35 2017
New Revision: 318356

URL: http://llvm.org/viewvc/llvm-project?rev=318356&view=rev
Log:
[globalisel][tablegen] Generate rule coverage and use it to identify untested rules

Summary:
This patch adds a LLVM_ENABLE_GISEL_COV which, like LLVM_ENABLE_DAGISEL_COV,
causes TableGen to instrument the generated table to collect rule coverage
information. However, LLVM_ENABLE_GISEL_COV goes a bit further than
LLVM_ENABLE_DAGISEL_COV. The information is written to files
(${CMAKE_BINARY_DIR}/gisel-coverage-* by default). These files can then be
concatenated into ${LLVM_GISEL_COV_PREFIX}-all after which TableGen will
read this information and use it to emit warnings about untested rules.

This technique could also be used by SelectionDAG and can be further
extended to detect hot rules and give them priority over colder rules.

Usage:
* Enable LLVM_ENABLE_GISEL_COV in CMake
* Build the compiler and run some tests
* cat gisel-coverage-[0-9]* > gisel-coverage-all
* Delete lib/Target/*/*GenGlobalISel.inc*
* Build the compiler

Known issues:
* ${LLVM_GISEL_COV_PREFIX}-all must be generated as a manual
  step due to a lack of a portable 'cat' command. It should be the
  concatenation of all ${LLVM_GISEL_COV_PREFIX}-[0-9]* files.
* There's no mechanism to discard coverage information when the ruleset
  changes

Depends on D39742

Reviewers: ab, qcolombet, t.p.northover, aditya_nandakumar, rovka

Reviewed By: rovka

Subscribers: vsk, arsenm, nhaehnle, mgorny, kristof.beyls, javed.absar, igorb, llvm-commits

Differential Revision: https://reviews.llvm.org/D39747

Added:
    llvm/trunk/include/llvm/Support/CodeGenCoverage.h
    llvm/trunk/lib/Support/CodeGenCoverage.cpp
    llvm/trunk/utils/llvm-gisel-cov.py
Modified:
    llvm/trunk/CMakeLists.txt
    llvm/trunk/cmake/modules/TableGen.cmake
    llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
    llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
    llvm/trunk/include/llvm/Config/config.h.cmake
    llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp
    llvm/trunk/lib/Support/CMakeLists.txt
    llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp
    llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
    llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h
    llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp
    llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp
    llvm/trunk/test/TableGen/GlobalISelEmitter.td
    llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp

Modified: llvm/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/CMakeLists.txt?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/CMakeLists.txt (original)
+++ llvm/trunk/CMakeLists.txt Wed Nov 15 16:46:35 2017
@@ -167,6 +167,10 @@ if(LLVM_DEPENDENCY_DEBUGGING)
 endif()
 
 option(LLVM_ENABLE_DAGISEL_COV "Debug: Prints tablegen patterns that were used for selecting" OFF)
+option(LLVM_ENABLE_GISEL_COV "Enable collection of GlobalISel rule coverage" OFF)
+if(LLVM_ENABLE_GISEL_COV)
+  set(LLVM_GISEL_COV_PREFIX "${CMAKE_BINARY_DIR}/gisel-coverage-" CACHE STRING "Provide a filename prefix to collect the GlobalISel rule coverage")
+endif()
 
 # Add path for custom modules
 set(CMAKE_MODULE_PATH

Modified: llvm/trunk/cmake/modules/TableGen.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/modules/TableGen.cmake?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/cmake/modules/TableGen.cmake (original)
+++ llvm/trunk/cmake/modules/TableGen.cmake Wed Nov 15 16:46:35 2017
@@ -52,6 +52,13 @@ function(tablegen project ofn)
       list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-coverage")
     endif()
   endif()
+  if (LLVM_ENABLE_GISEL_COV)
+    list(FIND ARGN "-gen-global-isel" idx)
+    if( NOT idx EQUAL -1 )
+      list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-gisel-coverage")
+      list(APPEND LLVM_TABLEGEN_FLAGS "-gisel-coverage-file=${LLVM_GISEL_COV_PREFIX}all")
+    endif()
+  endif()
 
   # We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the  DEPENDS list
   # (both the target and the file) to have .inc files rebuilt on

Modified: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h (original)
+++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h Wed Nov 15 16:46:35 2017
@@ -17,8 +17,9 @@
 #define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
 
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/CodeGenCoverage.h"
 #include <bitset>
 #include <cstddef>
 #include <cstdint>
@@ -33,6 +34,7 @@ class APFloat;
 class LLT;
 class MachineInstr;
 class MachineInstrBuilder;
+class MachineFunction;
 class MachineOperand;
 class MachineRegisterInfo;
 class RegisterBankInfo;
@@ -262,6 +264,10 @@ enum {
 
   /// A successful emission
   GIR_Done,
+
+  /// Increment the rule coverage counter.
+  /// - RuleID - The ID of the rule that was covered.
+  GIR_Coverage,
 };
 
 enum {
@@ -289,7 +295,7 @@ public:
   ///   if returns true:
   ///     for I in all mutated/inserted instructions:
   ///       !isPreISelGenericOpcode(I.getOpcode())
-  virtual bool select(MachineInstr &I) const = 0;
+  virtual bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const = 0;
 
 protected:
   using ComplexRendererFns =
@@ -328,8 +334,8 @@ protected:
       const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
       const int64_t *MatchTable, const TargetInstrInfo &TII,
       MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
-      const RegisterBankInfo &RBI,
-      const PredicateBitset &AvailableFeatures) const;
+      const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures,
+      CodeGenCoverage &CoverageInfo) const;
 
   /// Constrain a register operand of an instruction \p I to a specified
   /// register class. This could involve inserting COPYs before (for uses) or

Modified: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h (original)
+++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h Wed Nov 15 16:46:35 2017
@@ -49,8 +49,8 @@ bool InstructionSelector::executeMatchTa
     const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
     const int64_t *MatchTable, const TargetInstrInfo &TII,
     MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
-    const RegisterBankInfo &RBI,
-    const PredicateBitset &AvailableFeatures) const {
+    const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures,
+    CodeGenCoverage &CoverageInfo) const {
   uint64_t CurrentIdx = 0;
   SmallVector<uint64_t, 8> OnFailResumeAt;
 
@@ -677,6 +677,16 @@ bool InstructionSelector::executeMatchTa
       break;
     }
 
+    case GIR_Coverage: {
+      int64_t RuleID = MatchTable[CurrentIdx++];
+      CoverageInfo.setCovered(RuleID);
+
+      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
+                      dbgs()
+                          << CurrentIdx << ": GIR_Coverage(" << RuleID << ")");
+      break;
+    }
+
     case GIR_Done:
       DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                       dbgs() << CurrentIdx << ": GIR_Done");

Modified: llvm/trunk/include/llvm/Config/config.h.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Config/config.h.cmake?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Config/config.h.cmake (original)
+++ llvm/trunk/include/llvm/Config/config.h.cmake Wed Nov 15 16:46:35 2017
@@ -437,4 +437,10 @@
 /* Define to a function implementing strdup */
 #cmakedefine strdup ${strdup}
 
+/* Whether GlobalISel rule coverage is being collected */
+#cmakedefine01 LLVM_GISEL_COV_ENABLED
+
+/* Define to the default GlobalISel coverage file prefix */
+#cmakedefine LLVM_GISEL_COV_PREFIX "${LLVM_GISEL_COV_PREFIX}"
+
 #endif

Added: llvm/trunk/include/llvm/Support/CodeGenCoverage.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/CodeGenCoverage.h?rev=318356&view=auto
==============================================================================
--- llvm/trunk/include/llvm/Support/CodeGenCoverage.h (added)
+++ llvm/trunk/include/llvm/Support/CodeGenCoverage.h Wed Nov 15 16:46:35 2017
@@ -0,0 +1,37 @@
+//== llvm/Support/CodeGenCoverage.h ------------------------------*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file This file provides rule coverage tracking for tablegen-erated CodeGen.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H
+#define LLVM_SUPPORT_CODEGENCOVERAGE_H
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/Config/config.h"
+
+namespace llvm {
+class LLVMContext;
+
+class CodeGenCoverage {
+protected:
+  BitVector RuleCoverage;
+
+public:
+  CodeGenCoverage();
+
+  void setCovered(uint64_t RuleID);
+  bool isCovered(uint64_t RuleID);
+
+  bool parse(MemoryBuffer &Buffer, StringRef BackendName);
+  bool emit(StringRef FilePrefix, StringRef BackendName) const;
+  void reset();
+};
+} // end namespace llvm
+
+#endif // ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H

Modified: llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp (original)
+++ llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp Wed Nov 15 16:46:35 2017
@@ -20,10 +20,12 @@
 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/Config/config.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Function.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/TargetRegistry.h"
 #include "llvm/Target/TargetLowering.h"
 #include "llvm/Target/TargetSubtargetInfo.h"
 
@@ -31,6 +33,15 @@
 
 using namespace llvm;
 
+#ifdef LLVM_GISEL_COV_PREFIX
+static cl::opt<std::string>
+    CoveragePrefix("gisel-coverage-prefix", cl::init(LLVM_GISEL_COV_PREFIX),
+                   cl::desc("Record GlobalISel rule coverage files of this "
+                            "prefix if instrumentation was generated"));
+#else
+static const std::string CoveragePrefix = "";
+#endif
+
 char InstructionSelect::ID = 0;
 INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE,
                       "Select target instructions out of generic instructions",
@@ -66,6 +77,7 @@ bool InstructionSelect::runOnMachineFunc
 
   const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
   const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
+  CodeGenCoverage CoverageInfo;
   assert(ISel && "Cannot work without InstructionSelector");
 
   // An optimization remark emitter. Used to report failures.
@@ -127,7 +139,7 @@ bool InstructionSelect::runOnMachineFunc
         continue;
       }
 
-      if (!ISel->select(MI)) {
+      if (!ISel->select(MI, CoverageInfo)) {
         // FIXME: It would be nice to dump all inserted instructions.  It's
         // not obvious how, esp. considering select() can insert after MI.
         reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot select", MI);
@@ -187,6 +199,13 @@ bool InstructionSelect::runOnMachineFunc
   auto &TLI = *MF.getSubtarget().getTargetLowering();
   TLI.finalizeLowering(MF);
 
+  CoverageInfo.emit(CoveragePrefix,
+                    MF.getSubtarget()
+                        .getTargetLowering()
+                        ->getTargetMachine()
+                        .getTarget()
+                        .getBackendName());
+
   // FIXME: Should we accurately track changes?
   return true;
 }

Modified: llvm/trunk/lib/Support/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CMakeLists.txt?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Support/CMakeLists.txt (original)
+++ llvm/trunk/lib/Support/CMakeLists.txt Wed Nov 15 16:46:35 2017
@@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport
   circular_raw_ostream.cpp
   Chrono.cpp
   COM.cpp
+  CodeGenCoverage.cpp
   CommandLine.cpp
   Compression.cpp
   ConvertUTF.cpp

Added: llvm/trunk/lib/Support/CodeGenCoverage.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CodeGenCoverage.cpp?rev=318356&view=auto
==============================================================================
--- llvm/trunk/lib/Support/CodeGenCoverage.cpp (added)
+++ llvm/trunk/lib/Support/CodeGenCoverage.cpp Wed Nov 15 16:46:35 2017
@@ -0,0 +1,118 @@
+//===- lib/Support/CodeGenCoverage.cpp -------------------------------------==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the CodeGenCoverage class.
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CodeGenCoverage.h"
+
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#if LLVM_ON_UNIX
+#include <unistd.h>
+#elif LLVM_ON_WIN32
+#include <windows.h>
+#endif
+
+using namespace llvm;
+
+static sys::SmartMutex<true> OutputMutex;
+
+CodeGenCoverage::CodeGenCoverage() {}
+
+void CodeGenCoverage::setCovered(uint64_t RuleID) {
+  if (RuleCoverage.size() <= RuleID)
+    RuleCoverage.resize(RuleID + 1, 0);
+  RuleCoverage[RuleID] = true;
+}
+
+bool CodeGenCoverage::isCovered(uint64_t RuleID) {
+  if (RuleCoverage.size() <= RuleID)
+    return false;
+  return RuleCoverage[RuleID];
+}
+
+bool CodeGenCoverage::parse(MemoryBuffer &Buffer, StringRef BackendName) {
+  const char *CurPtr = Buffer.getBufferStart();
+
+  while (CurPtr != Buffer.getBufferEnd()) {
+    // Read the backend name from the input.
+    const char *LexedBackendName = CurPtr;
+    while (*CurPtr++ != 0)
+      ;
+    if (CurPtr == Buffer.getBufferEnd())
+      return false; // Data is invalid, expected rule id's to follow.
+
+    bool IsForThisBackend = BackendName.equals(LexedBackendName);
+    while (CurPtr != Buffer.getBufferEnd()) {
+      if (std::distance(CurPtr, Buffer.getBufferEnd()) < 8)
+        return false; // Data is invalid. Not enough bytes for another rule id.
+
+      uint64_t RuleID = support::endian::read64(CurPtr, support::native);
+      CurPtr += 8;
+
+      // ~0ull terminates the rule id list.
+      if (RuleID == ~0ull)
+        break;
+
+      // Anything else, is recorded or ignored depending on whether it's
+      // intended for the backend we're interested in.
+      if (IsForThisBackend)
+        setCovered(RuleID);
+    }
+  }
+
+  return true;
+}
+
+bool CodeGenCoverage::emit(StringRef CoveragePrefix,
+                           StringRef BackendName) const {
+  if (!CoveragePrefix.empty() && !RuleCoverage.empty()) {
+    sys::SmartScopedLock<true> Lock(OutputMutex);
+
+    // We can handle locking within a process easily enough but we don't want to
+    // manage it between multiple processes. Use the process ID to ensure no
+    // more than one process is ever writing to the same file at the same time.
+    std::string Pid =
+#if LLVM_ON_UNIX
+        llvm::to_string(::getpid());
+#elif LLVM_ON_WIN32
+        llvm::to_string(::GetCurrentProcessId());
+#else
+        "";
+#endif
+
+    std::string CoverageFilename = (CoveragePrefix + Pid).str();
+
+    std::error_code EC;
+    sys::fs::OpenFlags OpenFlags = sys::fs::F_Append;
+    std::unique_ptr<ToolOutputFile> CoverageFile =
+        llvm::make_unique<ToolOutputFile>(CoverageFilename, EC, OpenFlags);
+    if (EC)
+      return false;
+
+    uint64_t Zero = 0;
+    uint64_t InvZero = ~0ull;
+    CoverageFile->os() << BackendName;
+    CoverageFile->os().write((const char *)&Zero, sizeof(unsigned char));
+    for (uint64_t I : RuleCoverage.set_bits())
+      CoverageFile->os().write((const char *)&I, sizeof(uint64_t));
+    CoverageFile->os().write((const char *)&InvZero, sizeof(uint64_t));
+
+    CoverageFile->keep();
+  }
+
+  return true;
+}
+
+void CodeGenCoverage::reset() { RuleCoverage.resize(0); }

Modified: llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp (original)
+++ llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp Wed Nov 15 16:46:35 2017
@@ -48,13 +48,13 @@ public:
                              const AArch64Subtarget &STI,
                              const AArch64RegisterBankInfo &RBI);
 
-  bool select(MachineInstr &I) const override;
+  bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
   static const char *getName() { return DEBUG_TYPE; }
 
 private:
   /// tblgen-erated 'select' implementation, used as the initial selector for
   /// the patterns that don't require complex C++.
-  bool selectImpl(MachineInstr &I) const;
+  bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
 
   bool selectVaStartAAPCS(MachineInstr &I, MachineFunction &MF,
                           MachineRegisterInfo &MRI) const;
@@ -609,7 +609,8 @@ bool AArch64InstructionSelector::selectV
   return true;
 }
 
-bool AArch64InstructionSelector::select(MachineInstr &I) const {
+bool AArch64InstructionSelector::select(MachineInstr &I,
+                                        CodeGenCoverage &CoverageInfo) const {
   assert(I.getParent() && "Instruction should be in a basic block!");
   assert(I.getParent()->getParent() && "Instruction should be in a function!");
 
@@ -667,7 +668,7 @@ bool AArch64InstructionSelector::select(
     return false;
   }
 
-  if (selectImpl(I))
+  if (selectImpl(I, CoverageInfo))
     return true;
 
   LLT Ty =

Modified: llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp (original)
+++ llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp Wed Nov 15 16:46:35 2017
@@ -402,7 +402,8 @@ bool AMDGPUInstructionSelector::selectG_
   return Ret;
 }
 
-bool AMDGPUInstructionSelector::select(MachineInstr &I) const {
+bool AMDGPUInstructionSelector::select(MachineInstr &I,
+                                       CodeGenCoverage &CoverageInfo) const {
 
   if (!isPreISelGenericOpcode(I.getOpcode()))
     return true;

Modified: llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h (original)
+++ llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h Wed Nov 15 16:46:35 2017
@@ -35,7 +35,8 @@ public:
   AMDGPUInstructionSelector(const SISubtarget &STI,
                             const AMDGPURegisterBankInfo &RBI);
 
-  bool select(MachineInstr &I) const override;
+  bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
+
 private:
   struct GEPInfo {
     const MachineInstr &GEP;

Modified: llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp (original)
+++ llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp Wed Nov 15 16:46:35 2017
@@ -35,11 +35,11 @@ public:
   ARMInstructionSelector(const ARMBaseTargetMachine &TM, const ARMSubtarget &STI,
                          const ARMRegisterBankInfo &RBI);
 
-  bool select(MachineInstr &I) const override;
+  bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
   static const char *getName() { return DEBUG_TYPE; }
 
 private:
-  bool selectImpl(MachineInstr &I) const;
+  bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
 
   struct CmpConstants;
   struct InsertInfo;
@@ -653,7 +653,8 @@ bool ARMInstructionSelector::selectShift
   return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI);
 }
 
-bool ARMInstructionSelector::select(MachineInstr &I) const {
+bool ARMInstructionSelector::select(MachineInstr &I,
+                                    CodeGenCoverage &CoverageInfo) const {
   assert(I.getParent() && "Instruction should be in a basic block!");
   assert(I.getParent()->getParent() && "Instruction should be in a function!");
 
@@ -668,7 +669,7 @@ bool ARMInstructionSelector::select(Mach
     return true;
   }
 
-  if (selectImpl(I))
+  if (selectImpl(I, CoverageInfo))
     return true;
 
   MachineInstrBuilder MIB{MF, I};

Modified: llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp (original)
+++ llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp Wed Nov 15 16:46:35 2017
@@ -61,13 +61,13 @@ public:
   X86InstructionSelector(const X86TargetMachine &TM, const X86Subtarget &STI,
                          const X86RegisterBankInfo &RBI);
 
-  bool select(MachineInstr &I) const override;
+  bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
   static const char *getName() { return DEBUG_TYPE; }
 
 private:
   /// tblgen-erated 'select' implementation, used as the initial selector for
   /// the patterns that don't require complex C++.
-  bool selectImpl(MachineInstr &I) const;
+  bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
 
   // TODO: remove after supported by Tablegen-erated instruction selection.
   unsigned getLoadStoreOp(const LLT &Ty, const RegisterBank &RB, unsigned Opc,
@@ -93,9 +93,11 @@ private:
                    MachineFunction &MF) const;
   bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
   bool selectUnmergeValues(MachineInstr &I, MachineRegisterInfo &MRI,
-                           MachineFunction &MF) const;
+                           MachineFunction &MF,
+                           CodeGenCoverage &CoverageInfo) const;
   bool selectMergeValues(MachineInstr &I, MachineRegisterInfo &MRI,
-                         MachineFunction &MF) const;
+                         MachineFunction &MF,
+                         CodeGenCoverage &CoverageInfo) const;
   bool selectInsert(MachineInstr &I, MachineRegisterInfo &MRI,
                     MachineFunction &MF) const;
   bool selectExtract(MachineInstr &I, MachineRegisterInfo &MRI,
@@ -294,7 +296,8 @@ bool X86InstructionSelector::selectCopy(
   return true;
 }
 
-bool X86InstructionSelector::select(MachineInstr &I) const {
+bool X86InstructionSelector::select(MachineInstr &I,
+                                    CodeGenCoverage &CoverageInfo) const {
   assert(I.getParent() && "Instruction should be in a basic block!");
   assert(I.getParent()->getParent() && "Instruction should be in a function!");
 
@@ -318,7 +321,7 @@ bool X86InstructionSelector::select(Mach
   assert(I.getNumOperands() == I.getNumExplicitOperands() &&
          "Generic instruction has unexpected implicit operands\n");
 
-  if (selectImpl(I))
+  if (selectImpl(I, CoverageInfo))
     return true;
 
   DEBUG(dbgs() << " C++ instruction selection: "; I.print(dbgs()));
@@ -350,9 +353,9 @@ bool X86InstructionSelector::select(Mach
   case TargetOpcode::G_UADDE:
     return selectUadde(I, MRI, MF);
   case TargetOpcode::G_UNMERGE_VALUES:
-    return selectUnmergeValues(I, MRI, MF);
+    return selectUnmergeValues(I, MRI, MF, CoverageInfo);
   case TargetOpcode::G_MERGE_VALUES:
-    return selectMergeValues(I, MRI, MF);
+    return selectMergeValues(I, MRI, MF, CoverageInfo);
   case TargetOpcode::G_EXTRACT:
     return selectExtract(I, MRI, MF);
   case TargetOpcode::G_INSERT:
@@ -1093,9 +1096,9 @@ bool X86InstructionSelector::selectInser
   return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
 }
 
-bool X86InstructionSelector::selectUnmergeValues(MachineInstr &I,
-                                                 MachineRegisterInfo &MRI,
-                                                 MachineFunction &MF) const {
+bool X86InstructionSelector::selectUnmergeValues(
+    MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF,
+    CodeGenCoverage &CoverageInfo) const {
   assert((I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) &&
          "unexpected instruction");
 
@@ -1111,7 +1114,7 @@ bool X86InstructionSelector::selectUnmer
              .addReg(SrcReg)
              .addImm(Idx * DefSize);
 
-    if (!select(ExtrInst))
+    if (!select(ExtrInst, CoverageInfo))
       return false;
   }
 
@@ -1119,9 +1122,9 @@ bool X86InstructionSelector::selectUnmer
   return true;
 }
 
-bool X86InstructionSelector::selectMergeValues(MachineInstr &I,
-                                               MachineRegisterInfo &MRI,
-                                               MachineFunction &MF) const {
+bool X86InstructionSelector::selectMergeValues(
+    MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF,
+    CodeGenCoverage &CoverageInfo) const {
   assert((I.getOpcode() == TargetOpcode::G_MERGE_VALUES) &&
          "unexpected instruction");
 
@@ -1153,7 +1156,7 @@ bool X86InstructionSelector::selectMerge
 
     DefReg = Tmp;
 
-    if (!select(InsertInst))
+    if (!select(InsertInst, CoverageInfo))
       return false;
   }
 
@@ -1161,7 +1164,7 @@ bool X86InstructionSelector::selectMerge
                                     TII.get(TargetOpcode::COPY), DstReg)
                                 .addReg(DefReg);
 
-  if (!select(CopyInst))
+  if (!select(CopyInst, CoverageInfo))
     return false;
 
   I.eraseFromParent();

Modified: llvm/trunk/test/TableGen/GlobalISelEmitter.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/TableGen/GlobalISelEmitter.td?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/test/TableGen/GlobalISelEmitter.td (original)
+++ llvm/trunk/test/TableGen/GlobalISelEmitter.td Wed Nov 15 16:46:35 2017
@@ -153,7 +153,7 @@ def HasC : Predicate<"Subtarget->hasC()"
 // CHECK-NEXT:    &MyTargetInstructionSelector::selectComplexPatternRR, // gi_complex_rr
 // CHECK-NEXT:  }
 
-// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const {
+// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const {
 // CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent();
 // CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo();
 // CHECK:      AvailableFunctionFeatures = computeAvailableFunctionFeatures(&STI, &MF);
@@ -899,6 +899,6 @@ def BR : I<(outs), (ins unknown:$target)
 
 // CHECK-NEXT:    GIM_Reject,
 // CHECK-NEXT:  };
-// CHECK-NEXT:  if (executeMatchTable(*this, OutMIs, State, MatcherInfo, MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures)) {
+// CHECK-NEXT:  if (executeMatchTable(*this, OutMIs, State, MatcherInfo, MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) {
 // CHECK-NEXT:    return true;
 // CHECK-NEXT:  }

Modified: llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp?rev=318356&r1=318355&r2=318356&view=diff
==============================================================================
--- llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp (original)
+++ llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp Wed Nov 15 16:46:35 2017
@@ -36,6 +36,7 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineValueType.h"
+#include "llvm/Support/CodeGenCoverage.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/LowLevelTypeImpl.h"
@@ -43,8 +44,8 @@
 #include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
 #include "llvm/TableGen/TableGenBackend.h"
-#include <string>
 #include <numeric>
+#include <string>
 using namespace llvm;
 
 #define DEBUG_TYPE "gisel-emitter"
@@ -52,6 +53,7 @@ using namespace llvm;
 STATISTIC(NumPatternTotal, "Total number of patterns");
 STATISTIC(NumPatternImported, "Number of patterns imported from SelectionDAG");
 STATISTIC(NumPatternImportsSkipped, "Number of SelectionDAG imports skipped");
+STATISTIC(NumPatternsTested, "Number of patterns executed according to coverage information");
 STATISTIC(NumPatternEmitted, "Number of patterns emitted");
 
 cl::OptionCategory GlobalISelEmitterCat("Options for -gen-global-isel");
@@ -62,6 +64,16 @@ static cl::opt<bool> WarnOnSkippedPatter
              "in the GlobalISel selector"),
     cl::init(false), cl::cat(GlobalISelEmitterCat));
 
+static cl::opt<bool> GenerateCoverage(
+    "instrument-gisel-coverage",
+    cl::desc("Generate coverage instrumentation for GlobalISel"),
+    cl::init(false), cl::cat(GlobalISelEmitterCat));
+
+static cl::opt<std::string> UseCoverageFile(
+    "gisel-coverage-file", cl::init(""),
+    cl::desc("Specify file to retrieve coverage information from"),
+    cl::cat(GlobalISelEmitterCat));
+
 namespace {
 //===- Helper functions ---------------------------------------------------===//
 
@@ -569,14 +581,20 @@ protected:
   /// A map of Symbolic Names to ComplexPattern sub-operands.
   DefinedComplexPatternSubOperandMap ComplexSubOperands;
 
+  uint64_t RuleID;
+  static uint64_t NextRuleID;
+
 public:
   RuleMatcher(ArrayRef<SMLoc> SrcLoc)
       : Matchers(), Actions(), InsnVariableIDs(), MutatableInsns(),
         DefinedOperands(), NextInsnVarID(0), NextOutputInsnID(0),
-        NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands() {}
+        NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands(),
+        RuleID(NextRuleID++) {}
   RuleMatcher(RuleMatcher &&Other) = default;
   RuleMatcher &operator=(RuleMatcher &&Other) = default;
 
+  uint64_t getRuleID() const { return RuleID; }
+
   InstructionMatcher &addInstructionMatcher(StringRef SymbolicName);
   void addRequiredFeature(Record *Feature);
   const std::vector<Record *> &getRequiredFeatures() const;
@@ -664,6 +682,8 @@ public:
   unsigned allocateTempRegID() { return NextTempRegID++; }
 };
 
+uint64_t RuleMatcher::NextRuleID = 0;
+
 using action_iterator = RuleMatcher::action_iterator;
 
 template <class PredicateTy> class PredicateListMatcher {
@@ -2204,6 +2224,11 @@ void RuleMatcher::emit(MatchTable &Table
 
   for (const auto &MA : Actions)
     MA->emitActionOpcodes(Table, *this);
+
+  if (GenerateCoverage)
+    Table << MatchTable::Opcode("GIR_Coverage") << MatchTable::IntValue(RuleID)
+          << MatchTable::LineBreak;
+
   Table << MatchTable::Opcode("GIR_Done", -1) << MatchTable::LineBreak
         << MatchTable::Label(LabelID);
 }
@@ -2309,6 +2334,9 @@ private:
   // Map of predicates to their subtarget features.
   SubtargetFeatureInfoMap SubtargetFeatures;
 
+  // Rule coverage information.
+  Optional<CodeGenCoverage> RuleCoverage;
+
   void gatherNodeEquivs();
   Record *findNodeEquiv(Record *N) const;
 
@@ -3227,6 +3255,20 @@ void GlobalISelEmitter::emitImmPredicate
 }
 
 void GlobalISelEmitter::run(raw_ostream &OS) {
+  if (!UseCoverageFile.empty()) {
+    RuleCoverage = CodeGenCoverage();
+    auto RuleCoverageBufOrErr = MemoryBuffer::getFile(UseCoverageFile);
+    if (!RuleCoverageBufOrErr) {
+      PrintWarning(SMLoc(), "Missing rule coverage data");
+      RuleCoverage = None;
+    } else {
+      if (!RuleCoverage->parse(*RuleCoverageBufOrErr.get(), Target.getName())) {
+        PrintWarning(SMLoc(), "Ignoring invalid or missing rule coverage data");
+        RuleCoverage = None;
+      }
+    }
+  }
+
   // Track the GINodeEquiv definitions.
   gatherNodeEquivs();
 
@@ -3252,6 +3294,13 @@ void GlobalISelEmitter::run(raw_ostream
       continue;
     }
 
+    if (RuleCoverage) {
+      if (RuleCoverage->isCovered(MatcherOrErr->getRuleID()))
+        ++NumPatternsTested;
+      else
+        PrintWarning(Pat.getSrcRecord()->getLoc(),
+                     "Pattern is not covered by a test");
+    }
     Rules.push_back(std::move(MatcherOrErr.get()));
   }
 
@@ -3431,7 +3480,8 @@ void GlobalISelEmitter::run(raw_ostream
   OS << "};\n\n";
 
   OS << "bool " << Target.getName()
-     << "InstructionSelector::selectImpl(MachineInstr &I) const {\n"
+     << "InstructionSelector::selectImpl(MachineInstr &I, CodeGenCoverage "
+        "&CoverageInfo) const {\n"
      << "  MachineFunction &MF = *I.getParent()->getParent();\n"
      << "  MachineRegisterInfo &MRI = MF.getRegInfo();\n"
      << "  // FIXME: This should be computed on a per-function basis rather "
@@ -3452,7 +3502,7 @@ void GlobalISelEmitter::run(raw_ostream
   Table.emitDeclaration(OS);
   OS << "  if (executeMatchTable(*this, OutMIs, State, MatcherInfo, ";
   Table.emitUse(OS);
-  OS << ", TII, MRI, TRI, RBI, AvailableFeatures)) {\n"
+  OS << ", TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) {\n"
      << "    return true;\n"
      << "  }\n\n";
 

Added: llvm/trunk/utils/llvm-gisel-cov.py
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/llvm-gisel-cov.py?rev=318356&view=auto
==============================================================================
--- llvm/trunk/utils/llvm-gisel-cov.py (added)
+++ llvm/trunk/utils/llvm-gisel-cov.py Wed Nov 15 16:46:35 2017
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+"""
+Summarize the information in the given coverage files.
+
+Emits the number of rules covered or the percentage of rules covered depending
+on whether --num-rules has been used to specify the total number of rules.
+"""
+
+import argparse
+import struct
+
+class FileFormatError(Exception):
+  pass
+
+def backend_int_pair(s):
+  backend, sep, value = s.partition('=')
+  if (sep is None):
+    raise argparse.ArgumentTypeError("'=' missing, expected name=value")
+  if (not backend):
+    raise argparse.ArgumentTypeError("Expected name=value")
+  if (not value):
+    raise argparse.ArgumentTypeError("Expected name=value")
+  return backend, int(value)
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('input', nargs='+')
+  parser.add_argument('--num-rules', type=backend_int_pair, action='append',
+                      metavar='BACKEND=NUM',
+                      help='Specify the number of rules for a backend')
+  args = parser.parse_args()
+
+  covered_rules = {}
+
+  for input_filename in args.input:
+    with open(input_filename, 'rb') as input_fh:
+      data = input_fh.read()
+      pos = 0
+      while data:
+        backend, _, data = data.partition('\0')
+        pos += len(backend)
+        pos += 1
+
+        if len(backend) == 0:
+          raise FileFormatError()
+        backend, = struct.unpack("%ds" % len(backend), backend)
+
+        while data:
+          if len(data) < 8:
+            raise FileFormatError()
+          rule_id, = struct.unpack("Q", data[:8])
+          pos += 8
+          data = data[8:]
+          if rule_id == (2 ** 64) - 1:
+            break
+          covered_rules[backend] = covered_rules.get(backend, {})
+          covered_rules[backend][rule_id] = covered_rules[backend].get(rule_id, 0) + 1
+
+  num_rules = dict(args.num_rules)
+  for backend, rules_for_backend in covered_rules.items():
+    if backend in num_rules:
+      print "%s: %3.2f%% of rules covered" % (backend, (float(len(rules_for_backend.keys())) / num_rules[backend]) * 100)
+    else:
+      print "%s: %d rules covered" % (backend, len(rules_for_backend.keys()))
+
+if __name__ == '__main__':
+  main()




More information about the llvm-commits mailing list