9 changes: 5 additions & 4 deletions llvm/lib/Target/ARM/ARMInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class ARMInstructionSelector : public InstructionSelector {
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;
Expand Down Expand Up @@ -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!");

Expand All @@ -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};
Expand Down
37 changes: 20 additions & 17 deletions llvm/lib/Target/X86/X86InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ class X86InstructionSelector : public InstructionSelector {
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,
Expand All @@ -93,9 +93,11 @@ class X86InstructionSelector : public InstructionSelector {
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,
Expand Down Expand Up @@ -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!");

Expand All @@ -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()));
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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");

Expand All @@ -1111,17 +1114,17 @@ bool X86InstructionSelector::selectUnmergeValues(MachineInstr &I,
.addReg(SrcReg)
.addImm(Idx * DefSize);

if (!select(ExtrInst))
if (!select(ExtrInst, CoverageInfo))
return false;
}

I.eraseFromParent();
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");

Expand Down Expand Up @@ -1153,15 +1156,15 @@ bool X86InstructionSelector::selectMergeValues(MachineInstr &I,

DefReg = Tmp;

if (!select(InsertInst))
if (!select(InsertInst, CoverageInfo))
return false;
}

MachineInstr &CopyInst = *BuildMI(*I.getParent(), I, I.getDebugLoc(),
TII.get(TargetOpcode::COPY), DstReg)
.addReg(DefReg);

if (!select(CopyInst))
if (!select(CopyInst, CoverageInfo))
return false;

I.eraseFromParent();
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/TableGen/GlobalISelEmitter.td
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: }
58 changes: 54 additions & 4 deletions llvm/utils/TableGen/GlobalISelEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,24 @@
#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"
#include "llvm/Support/ScopedPrinter.h"
#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"

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");
Expand All @@ -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 ---------------------------------------------------===//

Expand Down Expand Up @@ -569,14 +581,20 @@ class RuleMatcher {
/// 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;
Expand Down Expand Up @@ -664,6 +682,8 @@ class RuleMatcher {
unsigned allocateTempRegID() { return NextTempRegID++; }
};

uint64_t RuleMatcher::NextRuleID = 0;

using action_iterator = RuleMatcher::action_iterator;

template <class PredicateTy> class PredicateListMatcher {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -2309,6 +2334,9 @@ class GlobalISelEmitter {
// Map of predicates to their subtarget features.
SubtargetFeatureInfoMap SubtargetFeatures;

// Rule coverage information.
Optional<CodeGenCoverage> RuleCoverage;

void gatherNodeEquivs();
Record *findNodeEquiv(Record *N) const;

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

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

Expand Down Expand Up @@ -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 "
Expand All @@ -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";

Expand Down
67 changes: 67 additions & 0 deletions llvm/utils/llvm-gisel-cov.py
Original file line number Diff line number Diff line change
@@ -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()