diff options
-rw-r--r-- | llvm/CMakeLists.txt | 4 | ||||
-rw-r--r-- | llvm/cmake/modules/TableGen.cmake | 7 | ||||
-rw-r--r-- | llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h | 14 | ||||
-rw-r--r-- | llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h | 14 | ||||
-rw-r--r-- | llvm/include/llvm/Config/config.h.cmake | 6 | ||||
-rw-r--r-- | llvm/include/llvm/Support/CodeGenCoverage.h | 37 | ||||
-rw-r--r-- | llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp | 21 | ||||
-rw-r--r-- | llvm/lib/Support/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/Support/CodeGenCoverage.cpp | 118 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp | 9 | ||||
-rw-r--r-- | llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h | 3 | ||||
-rw-r--r-- | llvm/lib/Target/ARM/ARMInstructionSelector.cpp | 9 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86InstructionSelector.cpp | 37 | ||||
-rw-r--r-- | llvm/test/TableGen/GlobalISelEmitter.td | 4 | ||||
-rw-r--r-- | llvm/utils/TableGen/GlobalISelEmitter.cpp | 58 | ||||
-rw-r--r-- | llvm/utils/llvm-gisel-cov.py | 67 |
17 files changed, 372 insertions, 40 deletions
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 53059e63d3d..8cd9d053c63 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -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 diff --git a/llvm/cmake/modules/TableGen.cmake b/llvm/cmake/modules/TableGen.cmake index 95de53a547b..d1afcb42f9d 100644 --- a/llvm/cmake/modules/TableGen.cmake +++ b/llvm/cmake/modules/TableGen.cmake @@ -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 diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h index ebfcec133b6..550e45a4be2 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -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 diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h index 0747205f447..1340a4cb7bc 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -49,8 +49,8 @@ bool InstructionSelector::executeMatchTable( 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::executeMatchTable( 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"); diff --git a/llvm/include/llvm/Config/config.h.cmake b/llvm/include/llvm/Config/config.h.cmake index 9bfe0891916..038f70a79f9 100644 --- a/llvm/include/llvm/Config/config.h.cmake +++ b/llvm/include/llvm/Config/config.h.cmake @@ -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 diff --git a/llvm/include/llvm/Support/CodeGenCoverage.h b/llvm/include/llvm/Support/CodeGenCoverage.h new file mode 100644 index 00000000000..1aa74fc3bcb --- /dev/null +++ b/llvm/include/llvm/Support/CodeGenCoverage.h @@ -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 diff --git a/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp b/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp index a16e14fe2db..66bd2dafb97 100644 --- a/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp +++ b/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp @@ -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::runOnMachineFunction(MachineFunction &MF) { 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::runOnMachineFunction(MachineFunction &MF) { 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::runOnMachineFunction(MachineFunction &MF) { 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; } diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 56aaf10ec2c..5d95a9a9a56 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport circular_raw_ostream.cpp Chrono.cpp COM.cpp + CodeGenCoverage.cpp CommandLine.cpp Compression.cpp ConvertUTF.cpp diff --git a/llvm/lib/Support/CodeGenCoverage.cpp b/llvm/lib/Support/CodeGenCoverage.cpp new file mode 100644 index 00000000000..5cc84cb96e8 --- /dev/null +++ b/llvm/lib/Support/CodeGenCoverage.cpp @@ -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); } diff --git a/llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp index 4f4e056cacb..c2d3ae31c62 100644 --- a/llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -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::selectVaStartDarwin( 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(MachineInstr &I) const { return false; } - if (selectImpl(I)) + if (selectImpl(I, CoverageInfo)) return true; LLT Ty = diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp index e54c887d609..16d240e9619 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp @@ -402,7 +402,8 @@ bool AMDGPUInstructionSelector::selectG_LOAD(MachineInstr &I) const { return Ret; } -bool AMDGPUInstructionSelector::select(MachineInstr &I) const { +bool AMDGPUInstructionSelector::select(MachineInstr &I, + CodeGenCoverage &CoverageInfo) const { if (!isPreISelGenericOpcode(I.getOpcode())) return true; diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h index ef845f44d36..715c4882f38 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h +++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h @@ -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; diff --git a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp index dd11b1d5f8d..4d286ed619f 100644 --- a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp +++ b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp @@ -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(unsigned ShiftOpc, 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(MachineInstr &I) const { return true; } - if (selectImpl(I)) + if (selectImpl(I, CoverageInfo)) return true; MachineInstrBuilder MIB{MF, I}; diff --git a/llvm/lib/Target/X86/X86InstructionSelector.cpp b/llvm/lib/Target/X86/X86InstructionSelector.cpp index 1efe95bf4e8..82e126f21f6 100644 --- a/llvm/lib/Target/X86/X86InstructionSelector.cpp +++ b/llvm/lib/Target/X86/X86InstructionSelector.cpp @@ -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(MachineInstr &I, 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(MachineInstr &I) const { 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(MachineInstr &I) const { 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::selectInsert(MachineInstr &I, 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::selectUnmergeValues(MachineInstr &I, .addReg(SrcReg) .addImm(Idx * DefSize); - if (!select(ExtrInst)) + if (!select(ExtrInst, CoverageInfo)) return false; } @@ -1119,9 +1122,9 @@ bool X86InstructionSelector::selectUnmergeValues(MachineInstr &I, 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::selectMergeValues(MachineInstr &I, DefReg = Tmp; - if (!select(InsertInst)) + if (!select(InsertInst, CoverageInfo)) return false; } @@ -1161,7 +1164,7 @@ bool X86InstructionSelector::selectMergeValues(MachineInstr &I, TII.get(TargetOpcode::COPY), DstReg) .addReg(DefReg); - if (!select(CopyInst)) + if (!select(CopyInst, CoverageInfo)) return false; I.eraseFromParent(); diff --git a/llvm/test/TableGen/GlobalISelEmitter.td b/llvm/test/TableGen/GlobalISelEmitter.td index f36240eec3c..296946fa52a 100644 --- a/llvm/test/TableGen/GlobalISelEmitter.td +++ b/llvm/test/TableGen/GlobalISelEmitter.td @@ -153,7 +153,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } // 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: } diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 83fa9c582e6..56a638483c0 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -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> WarnOnSkippedPatterns( "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::emitImmPredicates( } 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 &OS) { 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) { 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 &OS) { 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"; diff --git a/llvm/utils/llvm-gisel-cov.py b/llvm/utils/llvm-gisel-cov.py new file mode 100644 index 00000000000..a74ed10f864 --- /dev/null +++ b/llvm/utils/llvm-gisel-cov.py @@ -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() |