Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -181,6 +181,10 @@ 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 Index: cmake/modules/TableGen.cmake =================================================================== --- cmake/modules/TableGen.cmake +++ cmake/modules/TableGen.cmake @@ -52,6 +52,13 @@ 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 Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ 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 #include #include @@ -33,6 +34,7 @@ class LLT; class MachineInstr; class MachineInstrBuilder; +class MachineFunction; class MachineOperand; class MachineRegisterInfo; class RegisterBankInfo; @@ -262,6 +264,10 @@ /// 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 @@ /// 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 @@ const MatcherInfoTy &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 Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -49,8 +49,8 @@ const MatcherInfoTy &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 OnFailResumeAt; @@ -677,6 +677,16 @@ 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"); Index: include/llvm/Config/config.h.cmake =================================================================== --- include/llvm/Config/config.h.cmake +++ 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 Index: include/llvm/Support/CodeGenCoverage.h =================================================================== --- /dev/null +++ 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 Index: lib/CodeGen/GlobalISel/InstructionSelect.cpp =================================================================== --- lib/CodeGen/GlobalISel/InstructionSelect.cpp +++ 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,11 @@ using namespace llvm; +static cl::opt + CoveragePrefix("gisel-coverage-prefix", cl::init(LLVM_GISEL_COV_PREFIX), + cl::desc("Record GlobalISel rule coverage files of this " + "prefix if instrumentation was generated")); + char InstructionSelect::ID = 0; INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE, "Select target instructions out of generic instructions", @@ -66,6 +73,7 @@ const TargetPassConfig &TPC = getAnalysis(); const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector(); + CodeGenCoverage CoverageInfo; assert(ISel && "Cannot work without InstructionSelector"); // An optimization remark emitter. Used to report failures. @@ -127,7 +135,7 @@ 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 +195,13 @@ 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; } Index: lib/Support/CMakeLists.txt =================================================================== --- lib/Support/CMakeLists.txt +++ lib/Support/CMakeLists.txt @@ -48,6 +48,7 @@ circular_raw_ostream.cpp Chrono.cpp COM.cpp + CodeGenCoverage.cpp CommandLine.cpp Compression.cpp ConvertUTF.cpp Index: lib/Support/CodeGenCoverage.cpp =================================================================== --- /dev/null +++ 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 +#elif LLVM_ON_WIN32 +#include +#endif + +using namespace llvm; + +static sys::SmartMutex 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 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 CoverageFile = + llvm::make_unique(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); } Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -48,13 +48,13 @@ 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 @@ 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 @@ return false; } - if (selectImpl(I)) + if (selectImpl(I, CoverageInfo)) return true; LLT Ty = Index: lib/Target/AMDGPU/AMDGPUInstructionSelector.h =================================================================== --- lib/Target/AMDGPU/AMDGPUInstructionSelector.h +++ lib/Target/AMDGPU/AMDGPUInstructionSelector.h @@ -35,7 +35,8 @@ 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; Index: lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp +++ lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp @@ -402,7 +402,8 @@ return Ret; } -bool AMDGPUInstructionSelector::select(MachineInstr &I) const { +bool AMDGPUInstructionSelector::select(MachineInstr &I, + CodeGenCoverage &CoverageInfo) const { if (!isPreISelGenericOpcode(I.getOpcode())) return true; Index: lib/Target/ARM/ARMInstructionSelector.cpp =================================================================== --- lib/Target/ARM/ARMInstructionSelector.cpp +++ lib/Target/ARM/ARMInstructionSelector.cpp @@ -35,11 +35,11 @@ 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; @@ -649,7 +649,8 @@ 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!"); @@ -664,7 +665,7 @@ return true; } - if (selectImpl(I)) + if (selectImpl(I, CoverageInfo)) return true; MachineInstrBuilder MIB{MF, I}; Index: lib/Target/X86/X86InstructionSelector.cpp =================================================================== --- lib/Target/X86/X86InstructionSelector.cpp +++ lib/Target/X86/X86InstructionSelector.cpp @@ -61,13 +61,13 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ .addReg(SrcReg) .addImm(Idx * DefSize); - if (!select(ExtrInst)) + if (!select(ExtrInst, CoverageInfo)) return false; } @@ -1119,9 +1122,9 @@ 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 @@ DefReg = Tmp; - if (!select(InsertInst)) + if (!select(InsertInst, CoverageInfo)) return false; } @@ -1161,7 +1164,7 @@ TII.get(TargetOpcode::COPY), DstReg) .addReg(DefReg); - if (!select(CopyInst)) + if (!select(CopyInst, CoverageInfo)) return false; I.eraseFromParent(); Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -151,7 +151,7 @@ // 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); @@ -863,6 +863,6 @@ // 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: } Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ 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 #include +#include using namespace llvm; #define DEBUG_TYPE "gisel-emitter" @@ -52,6 +53,7 @@ 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 @@ "in the GlobalISel selector"), cl::init(false), cl::cat(GlobalISelEmitterCat)); +static cl::opt GenerateCoverage( + "instrument-gisel-coverage", + cl::desc("Generate coverage instrumentation for GlobalISel"), + cl::init(false), cl::cat(GlobalISelEmitterCat)); + +static cl::opt 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 @@ /// A map of Symbolic Names to ComplexPattern sub-operands. DefinedComplexPatternSubOperandMap ComplexSubOperands; + uint64_t RuleID; + static uint64_t NextRuleID; + public: RuleMatcher(ArrayRef 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 &getRequiredFeatures() const; @@ -664,6 +682,8 @@ unsigned allocateTempRegID() { return NextTempRegID++; } }; +uint64_t RuleMatcher::NextRuleID = 0; + using action_iterator = RuleMatcher::action_iterator; template class PredicateListMatcher { @@ -2204,6 +2224,11 @@ 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 @@ // Map of predicates to their subtarget features. SubtargetFeatureInfoMap SubtargetFeatures; + // Rule coverage information. + Optional RuleCoverage; + void gatherNodeEquivs(); Record *findNodeEquiv(Record *N) const; @@ -3221,6 +3249,20 @@ } 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(); @@ -3245,6 +3287,13 @@ 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())); } @@ -3424,7 +3473,8 @@ 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 " @@ -3445,7 +3495,7 @@ 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"; Index: utils/llvm-gisel-cov.py =================================================================== --- /dev/null +++ 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()