1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00

[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

llvm-svn: 318356
This commit is contained in:
Daniel Sanders 2017-11-16 00:46:35 +00:00
parent 244630215c
commit f521e30212
17 changed files with 372 additions and 40 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport
circular_raw_ostream.cpp
Chrono.cpp
COM.cpp
CodeGenCoverage.cpp
CommandLine.cpp
Compression.cpp
ConvertUTF.cpp

View File

@ -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); }

View File

@ -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 =

View File

@ -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;

View File

@ -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;

View File

@ -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};

View File

@ -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();

View File

@ -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: }

View File

@ -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";

67
utils/llvm-gisel-cov.py Normal file
View File

@ -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()