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:
parent
244630215c
commit
f521e30212
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
37
include/llvm/Support/CodeGenCoverage.h
Normal file
37
include/llvm/Support/CodeGenCoverage.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport
|
||||
circular_raw_ostream.cpp
|
||||
Chrono.cpp
|
||||
COM.cpp
|
||||
CodeGenCoverage.cpp
|
||||
CommandLine.cpp
|
||||
Compression.cpp
|
||||
ConvertUTF.cpp
|
||||
|
118
lib/Support/CodeGenCoverage.cpp
Normal file
118
lib/Support/CodeGenCoverage.cpp
Normal 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); }
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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();
|
||||
|
@ -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: }
|
||||
|
@ -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
67
utils/llvm-gisel-cov.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user