Skip to content

Commit f76f315

Browse files
committed
[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
1 parent 8d8a8bb commit f76f315

17 files changed

+372
-40
lines changed

llvm/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ if(LLVM_DEPENDENCY_DEBUGGING)
167167
endif()
168168

169169
option(LLVM_ENABLE_DAGISEL_COV "Debug: Prints tablegen patterns that were used for selecting" OFF)
170+
option(LLVM_ENABLE_GISEL_COV "Enable collection of GlobalISel rule coverage" OFF)
171+
if(LLVM_ENABLE_GISEL_COV)
172+
set(LLVM_GISEL_COV_PREFIX "${CMAKE_BINARY_DIR}/gisel-coverage-" CACHE STRING "Provide a filename prefix to collect the GlobalISel rule coverage")
173+
endif()
170174

171175
# Add path for custom modules
172176
set(CMAKE_MODULE_PATH

llvm/cmake/modules/TableGen.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ function(tablegen project ofn)
5252
list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-coverage")
5353
endif()
5454
endif()
55+
if (LLVM_ENABLE_GISEL_COV)
56+
list(FIND ARGN "-gen-global-isel" idx)
57+
if( NOT idx EQUAL -1 )
58+
list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-gisel-coverage")
59+
list(APPEND LLVM_TABLEGEN_FLAGS "-gisel-coverage-file=${LLVM_GISEL_COV_PREFIX}all")
60+
endif()
61+
endif()
5562

5663
# We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the DEPENDS list
5764
# (both the target and the file) to have .inc files rebuilt on

llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
1818

1919
#include "llvm/ADT/DenseMap.h"
20-
#include "llvm/ADT/SmallVector.h"
2120
#include "llvm/ADT/Optional.h"
21+
#include "llvm/ADT/SmallVector.h"
22+
#include "llvm/Support/CodeGenCoverage.h"
2223
#include <bitset>
2324
#include <cstddef>
2425
#include <cstdint>
@@ -33,6 +34,7 @@ class APFloat;
3334
class LLT;
3435
class MachineInstr;
3536
class MachineInstrBuilder;
37+
class MachineFunction;
3638
class MachineOperand;
3739
class MachineRegisterInfo;
3840
class RegisterBankInfo;
@@ -262,6 +264,10 @@ enum {
262264

263265
/// A successful emission
264266
GIR_Done,
267+
268+
/// Increment the rule coverage counter.
269+
/// - RuleID - The ID of the rule that was covered.
270+
GIR_Coverage,
265271
};
266272

267273
enum {
@@ -289,7 +295,7 @@ class InstructionSelector {
289295
/// if returns true:
290296
/// for I in all mutated/inserted instructions:
291297
/// !isPreISelGenericOpcode(I.getOpcode())
292-
virtual bool select(MachineInstr &I) const = 0;
298+
virtual bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const = 0;
293299

294300
protected:
295301
using ComplexRendererFns =
@@ -328,8 +334,8 @@ class InstructionSelector {
328334
const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
329335
const int64_t *MatchTable, const TargetInstrInfo &TII,
330336
MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
331-
const RegisterBankInfo &RBI,
332-
const PredicateBitset &AvailableFeatures) const;
337+
const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures,
338+
CodeGenCoverage &CoverageInfo) const;
333339

334340
/// Constrain a register operand of an instruction \p I to a specified
335341
/// register class. This could involve inserting COPYs before (for uses) or

llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ bool InstructionSelector::executeMatchTable(
4949
const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn> &MatcherInfo,
5050
const int64_t *MatchTable, const TargetInstrInfo &TII,
5151
MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
52-
const RegisterBankInfo &RBI,
53-
const PredicateBitset &AvailableFeatures) const {
52+
const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures,
53+
CodeGenCoverage &CoverageInfo) const {
5454
uint64_t CurrentIdx = 0;
5555
SmallVector<uint64_t, 8> OnFailResumeAt;
5656

@@ -677,6 +677,16 @@ bool InstructionSelector::executeMatchTable(
677677
break;
678678
}
679679

680+
case GIR_Coverage: {
681+
int64_t RuleID = MatchTable[CurrentIdx++];
682+
CoverageInfo.setCovered(RuleID);
683+
684+
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
685+
dbgs()
686+
<< CurrentIdx << ": GIR_Coverage(" << RuleID << ")");
687+
break;
688+
}
689+
680690
case GIR_Done:
681691
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
682692
dbgs() << CurrentIdx << ": GIR_Done");

llvm/include/llvm/Config/config.h.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,10 @@
437437
/* Define to a function implementing strdup */
438438
#cmakedefine strdup ${strdup}
439439

440+
/* Whether GlobalISel rule coverage is being collected */
441+
#cmakedefine01 LLVM_GISEL_COV_ENABLED
442+
443+
/* Define to the default GlobalISel coverage file prefix */
444+
#cmakedefine LLVM_GISEL_COV_PREFIX "${LLVM_GISEL_COV_PREFIX}"
445+
440446
#endif
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//== llvm/Support/CodeGenCoverage.h ------------------------------*- C++ -*-==//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
/// \file This file provides rule coverage tracking for tablegen-erated CodeGen.
10+
//===----------------------------------------------------------------------===//
11+
12+
#ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H
13+
#define LLVM_SUPPORT_CODEGENCOVERAGE_H
14+
15+
#include "llvm/ADT/BitVector.h"
16+
#include "llvm/Config/config.h"
17+
18+
namespace llvm {
19+
class LLVMContext;
20+
21+
class CodeGenCoverage {
22+
protected:
23+
BitVector RuleCoverage;
24+
25+
public:
26+
CodeGenCoverage();
27+
28+
void setCovered(uint64_t RuleID);
29+
bool isCovered(uint64_t RuleID);
30+
31+
bool parse(MemoryBuffer &Buffer, StringRef BackendName);
32+
bool emit(StringRef FilePrefix, StringRef BackendName) const;
33+
void reset();
34+
};
35+
} // end namespace llvm
36+
37+
#endif // ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H

llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,28 @@
2020
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
2121
#include "llvm/CodeGen/MachineRegisterInfo.h"
2222
#include "llvm/CodeGen/TargetPassConfig.h"
23+
#include "llvm/Config/config.h"
2324
#include "llvm/IR/Constants.h"
2425
#include "llvm/IR/Function.h"
2526
#include "llvm/Support/CommandLine.h"
2627
#include "llvm/Support/Debug.h"
28+
#include "llvm/Support/TargetRegistry.h"
2729
#include "llvm/Target/TargetLowering.h"
2830
#include "llvm/Target/TargetSubtargetInfo.h"
2931

3032
#define DEBUG_TYPE "instruction-select"
3133

3234
using namespace llvm;
3335

36+
#ifdef LLVM_GISEL_COV_PREFIX
37+
static cl::opt<std::string>
38+
CoveragePrefix("gisel-coverage-prefix", cl::init(LLVM_GISEL_COV_PREFIX),
39+
cl::desc("Record GlobalISel rule coverage files of this "
40+
"prefix if instrumentation was generated"));
41+
#else
42+
static const std::string CoveragePrefix = "";
43+
#endif
44+
3445
char InstructionSelect::ID = 0;
3546
INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE,
3647
"Select target instructions out of generic instructions",
@@ -66,6 +77,7 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
6677

6778
const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
6879
const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
80+
CodeGenCoverage CoverageInfo;
6981
assert(ISel && "Cannot work without InstructionSelector");
7082

7183
// An optimization remark emitter. Used to report failures.
@@ -127,7 +139,7 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
127139
continue;
128140
}
129141

130-
if (!ISel->select(MI)) {
142+
if (!ISel->select(MI, CoverageInfo)) {
131143
// FIXME: It would be nice to dump all inserted instructions. It's
132144
// not obvious how, esp. considering select() can insert after MI.
133145
reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot select", MI);
@@ -187,6 +199,13 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
187199
auto &TLI = *MF.getSubtarget().getTargetLowering();
188200
TLI.finalizeLowering(MF);
189201

202+
CoverageInfo.emit(CoveragePrefix,
203+
MF.getSubtarget()
204+
.getTargetLowering()
205+
->getTargetMachine()
206+
.getTarget()
207+
.getBackendName());
208+
190209
// FIXME: Should we accurately track changes?
191210
return true;
192211
}

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport
4848
circular_raw_ostream.cpp
4949
Chrono.cpp
5050
COM.cpp
51+
CodeGenCoverage.cpp
5152
CommandLine.cpp
5253
Compression.cpp
5354
ConvertUTF.cpp

llvm/lib/Support/CodeGenCoverage.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//===- lib/Support/CodeGenCoverage.cpp -------------------------------------==//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
/// \file
10+
/// This file implements the CodeGenCoverage class.
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Support/CodeGenCoverage.h"
14+
15+
#include "llvm/Support/Endian.h"
16+
#include "llvm/Support/FileSystem.h"
17+
#include "llvm/Support/Mutex.h"
18+
#include "llvm/Support/ScopedPrinter.h"
19+
#include "llvm/Support/ToolOutputFile.h"
20+
21+
#if LLVM_ON_UNIX
22+
#include <unistd.h>
23+
#elif LLVM_ON_WIN32
24+
#include <windows.h>
25+
#endif
26+
27+
using namespace llvm;
28+
29+
static sys::SmartMutex<true> OutputMutex;
30+
31+
CodeGenCoverage::CodeGenCoverage() {}
32+
33+
void CodeGenCoverage::setCovered(uint64_t RuleID) {
34+
if (RuleCoverage.size() <= RuleID)
35+
RuleCoverage.resize(RuleID + 1, 0);
36+
RuleCoverage[RuleID] = true;
37+
}
38+
39+
bool CodeGenCoverage::isCovered(uint64_t RuleID) {
40+
if (RuleCoverage.size() <= RuleID)
41+
return false;
42+
return RuleCoverage[RuleID];
43+
}
44+
45+
bool CodeGenCoverage::parse(MemoryBuffer &Buffer, StringRef BackendName) {
46+
const char *CurPtr = Buffer.getBufferStart();
47+
48+
while (CurPtr != Buffer.getBufferEnd()) {
49+
// Read the backend name from the input.
50+
const char *LexedBackendName = CurPtr;
51+
while (*CurPtr++ != 0)
52+
;
53+
if (CurPtr == Buffer.getBufferEnd())
54+
return false; // Data is invalid, expected rule id's to follow.
55+
56+
bool IsForThisBackend = BackendName.equals(LexedBackendName);
57+
while (CurPtr != Buffer.getBufferEnd()) {
58+
if (std::distance(CurPtr, Buffer.getBufferEnd()) < 8)
59+
return false; // Data is invalid. Not enough bytes for another rule id.
60+
61+
uint64_t RuleID = support::endian::read64(CurPtr, support::native);
62+
CurPtr += 8;
63+
64+
// ~0ull terminates the rule id list.
65+
if (RuleID == ~0ull)
66+
break;
67+
68+
// Anything else, is recorded or ignored depending on whether it's
69+
// intended for the backend we're interested in.
70+
if (IsForThisBackend)
71+
setCovered(RuleID);
72+
}
73+
}
74+
75+
return true;
76+
}
77+
78+
bool CodeGenCoverage::emit(StringRef CoveragePrefix,
79+
StringRef BackendName) const {
80+
if (!CoveragePrefix.empty() && !RuleCoverage.empty()) {
81+
sys::SmartScopedLock<true> Lock(OutputMutex);
82+
83+
// We can handle locking within a process easily enough but we don't want to
84+
// manage it between multiple processes. Use the process ID to ensure no
85+
// more than one process is ever writing to the same file at the same time.
86+
std::string Pid =
87+
#if LLVM_ON_UNIX
88+
llvm::to_string(::getpid());
89+
#elif LLVM_ON_WIN32
90+
llvm::to_string(::GetCurrentProcessId());
91+
#else
92+
"";
93+
#endif
94+
95+
std::string CoverageFilename = (CoveragePrefix + Pid).str();
96+
97+
std::error_code EC;
98+
sys::fs::OpenFlags OpenFlags = sys::fs::F_Append;
99+
std::unique_ptr<ToolOutputFile> CoverageFile =
100+
llvm::make_unique<ToolOutputFile>(CoverageFilename, EC, OpenFlags);
101+
if (EC)
102+
return false;
103+
104+
uint64_t Zero = 0;
105+
uint64_t InvZero = ~0ull;
106+
CoverageFile->os() << BackendName;
107+
CoverageFile->os().write((const char *)&Zero, sizeof(unsigned char));
108+
for (uint64_t I : RuleCoverage.set_bits())
109+
CoverageFile->os().write((const char *)&I, sizeof(uint64_t));
110+
CoverageFile->os().write((const char *)&InvZero, sizeof(uint64_t));
111+
112+
CoverageFile->keep();
113+
}
114+
115+
return true;
116+
}
117+
118+
void CodeGenCoverage::reset() { RuleCoverage.resize(0); }

llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ class AArch64InstructionSelector : public InstructionSelector {
4848
const AArch64Subtarget &STI,
4949
const AArch64RegisterBankInfo &RBI);
5050

51-
bool select(MachineInstr &I) const override;
51+
bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
5252
static const char *getName() { return DEBUG_TYPE; }
5353

5454
private:
5555
/// tblgen-erated 'select' implementation, used as the initial selector for
5656
/// the patterns that don't require complex C++.
57-
bool selectImpl(MachineInstr &I) const;
57+
bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
5858

5959
bool selectVaStartAAPCS(MachineInstr &I, MachineFunction &MF,
6060
MachineRegisterInfo &MRI) const;
@@ -609,7 +609,8 @@ bool AArch64InstructionSelector::selectVaStartDarwin(
609609
return true;
610610
}
611611

612-
bool AArch64InstructionSelector::select(MachineInstr &I) const {
612+
bool AArch64InstructionSelector::select(MachineInstr &I,
613+
CodeGenCoverage &CoverageInfo) const {
613614
assert(I.getParent() && "Instruction should be in a basic block!");
614615
assert(I.getParent()->getParent() && "Instruction should be in a function!");
615616

@@ -667,7 +668,7 @@ bool AArch64InstructionSelector::select(MachineInstr &I) const {
667668
return false;
668669
}
669670

670-
if (selectImpl(I))
671+
if (selectImpl(I, CoverageInfo))
671672
return true;
672673

673674
LLT Ty =

llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,8 @@ bool AMDGPUInstructionSelector::selectG_LOAD(MachineInstr &I) const {
402402
return Ret;
403403
}
404404

405-
bool AMDGPUInstructionSelector::select(MachineInstr &I) const {
405+
bool AMDGPUInstructionSelector::select(MachineInstr &I,
406+
CodeGenCoverage &CoverageInfo) const {
406407

407408
if (!isPreISelGenericOpcode(I.getOpcode()))
408409
return true;

llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class AMDGPUInstructionSelector : public InstructionSelector {
3535
AMDGPUInstructionSelector(const SISubtarget &STI,
3636
const AMDGPURegisterBankInfo &RBI);
3737

38-
bool select(MachineInstr &I) const override;
38+
bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override;
39+
3940
private:
4041
struct GEPInfo {
4142
const MachineInstr &GEP;

0 commit comments

Comments
 (0)