[MC][TableGen] Add optional libpfm counter names for ProcResUnits.

Summary:
Subtargets can define the libpfm counter names that can be used to
measure cycles and uops issued on ProcResUnits.
This allows making llvm-exegesis available on more targets.
Fixes PR36984.

Reviewers: gchatelet, RKSimon, andreadb, craig.topper

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D45360

llvm-svn: 329675
This commit is contained in:
Clement Courbet 2018-04-10 08:16:37 +00:00
parent c9308fd1bc
commit 221e6ccd99
9 changed files with 218 additions and 54 deletions

View File

@ -172,6 +172,18 @@ struct MCExtraProcessorInfo {
unsigned NumRegisterFiles;
const MCRegisterCostEntry *RegisterCostTable;
unsigned NumRegisterCostEntries;
struct PfmCountersInfo {
// An optional name of a performance counter that can be used to measure
// cycles.
const char *CycleCounter;
// For each MCProcResourceDesc defined by the processor, an optional list of
// names of performance counters that can be used to measure the resource
// utilization.
const char **IssueCounters;
};
PfmCountersInfo PfmCounters;
};
/// Machine model for scheduling, bundling, and heuristics.

View File

@ -182,7 +182,8 @@ class ProcResourceKind;
//
// SchedModel ties these units to a processor for any stand-alone defs
// of this class.
class ProcResourceUnits<ProcResourceKind kind, int num> {
class ProcResourceUnits<ProcResourceKind kind, int num,
list<string> pfmCounters> {
ProcResourceKind Kind = kind;
int NumUnits = num;
ProcResourceKind Super = ?;
@ -197,8 +198,8 @@ def EponymousProcResourceKind : ProcResourceKind;
// Subtargets typically define processor resource kind and number of
// units in one place.
class ProcResource<int num> : ProcResourceKind,
ProcResourceUnits<EponymousProcResourceKind, num>;
class ProcResource<int num, list<string> pfmCounters = []> : ProcResourceKind,
ProcResourceUnits<EponymousProcResourceKind, num, pfmCounters>;
class ProcResGroup<list<ProcResource> resources> : ProcResourceKind {
list<ProcResource> Resources = resources;
@ -476,3 +477,23 @@ class RetireControlUnit<int bufferSize, int retirePerCycle> {
SchedMachineModel SchedModel = ?;
}
// Allow the definition of hardware counters.
class PfmCounter {
SchedMachineModel SchedModel = ?;
}
// Each processor can define how to measure cycles by defining a
// PfmCycleCounter.
class PfmCycleCounter<string counter> : PfmCounter {
string Counter = counter;
}
// Each ProcResourceUnits can define how to measure issued uops by defining
// a PfmIssueCounter.
class PfmIssueCounter<ProcResourceUnits resource, list<string> counters>
: PfmCounter{
// The resource units on which uops are issued.
ProcResourceUnits Resource = resource;
// The list of counters that measure issue events.
list<string> Counters = counters;
}

View File

@ -1132,3 +1132,9 @@ def X86 : Target {
let AssemblyWriters = [ATTAsmWriter, IntelAsmWriter];
let AllowRegisterRenaming = 1;
}
//===----------------------------------------------------------------------===//
// Pfm Counters
//===----------------------------------------------------------------------===//
include "X86PfmCounters.td"

View File

@ -0,0 +1,71 @@
//===-- X86PfmCounters.td - X86 Hardware Counters ----------*- tablegen -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This describes the available hardware counters for various subtargets.
//
//===----------------------------------------------------------------------===//
let SchedModel = SandyBridgeModel in {
def SBCycleCounter : PfmCycleCounter<"unhalted_core_cycles">;
def SBPort0Counter : PfmIssueCounter<SBPort0, ["uops_dispatched_port:port_0"]>;
def SBPort1Counter : PfmIssueCounter<SBPort1, ["uops_dispatched_port:port_1"]>;
def SBPort23Counter : PfmIssueCounter<SBPort23,
["uops_dispatched_port:port_2",
"uops_dispatched_port:port_3"]>;
def SBPort4Counter : PfmIssueCounter<SBPort4, ["uops_dispatched_port:port_4"]>;
def SBPort5Counter : PfmIssueCounter<SBPort5, ["uops_dispatched_port:port_5"]>;
}
let SchedModel = HaswellModel in {
def HWCycleCounter : PfmCycleCounter<"unhalted_core_cycles">;
def HWPort0Counter : PfmIssueCounter<HWPort0, ["uops_dispatched_port:port_0"]>;
def HWPort1Counter : PfmIssueCounter<HWPort1, ["uops_dispatched_port:port_1"]>;
def HWPort2Counter : PfmIssueCounter<HWPort2, ["uops_dispatched_port:port_2"]>;
def HWPort3Counter : PfmIssueCounter<HWPort3, ["uops_dispatched_port:port_3"]>;
def HWPort4Counter : PfmIssueCounter<HWPort4, ["uops_dispatched_port:port_4"]>;
def HWPort5Counter : PfmIssueCounter<HWPort5, ["uops_dispatched_port:port_5"]>;
def HWPort6Counter : PfmIssueCounter<HWPort6, ["uops_dispatched_port:port_6"]>;
def HWPort7Counter : PfmIssueCounter<HWPort7, ["uops_dispatched_port:port_7"]>;
}
let SchedModel = BroadwellModel in {
def BWCycleCounter : PfmCycleCounter<"unhalted_core_cycles">;
def BWPort0Counter : PfmIssueCounter<BWPort0, ["uops_dispatched_port:port_0"]>;
def BWPort1Counter : PfmIssueCounter<BWPort1, ["uops_dispatched_port:port_1"]>;
def BWPort2Counter : PfmIssueCounter<BWPort2, ["uops_dispatched_port:port_2"]>;
def BWPort3Counter : PfmIssueCounter<BWPort3, ["uops_dispatched_port:port_3"]>;
def BWPort4Counter : PfmIssueCounter<BWPort4, ["uops_dispatched_port:port_4"]>;
def BWPort5Counter : PfmIssueCounter<BWPort5, ["uops_dispatched_port:port_5"]>;
def BWPort6Counter : PfmIssueCounter<BWPort6, ["uops_dispatched_port:port_6"]>;
def BWPort7Counter : PfmIssueCounter<BWPort7, ["uops_dispatched_port:port_7"]>;
}
let SchedModel = SkylakeClientModel in {
def SKLCycleCounter : PfmCycleCounter<"unhalted_core_cycles">;
def SKLPort0Counter : PfmIssueCounter<SKLPort0, ["uops_dispatched_port:port_0"]>;
def SKLPort1Counter : PfmIssueCounter<SKLPort1, ["uops_dispatched_port:port_1"]>;
def SKLPort2Counter : PfmIssueCounter<SKLPort2, ["uops_dispatched_port:port_2"]>;
def SKLPort3Counter : PfmIssueCounter<SKLPort3, ["uops_dispatched_port:port_3"]>;
def SKLPort4Counter : PfmIssueCounter<SKLPort4, ["uops_dispatched_port:port_4"]>;
def SKLPort5Counter : PfmIssueCounter<SKLPort5, ["uops_dispatched_port:port_5"]>;
def SKLPort6Counter : PfmIssueCounter<SKLPort6, ["uops_dispatched_port:port_6"]>;
def SKLPort7Counter : PfmIssueCounter<SKLPort7, ["uops_dispatched_port:port_7"]>;
}
let SchedModel = SkylakeServerModel in {
def SKXCycleCounter : PfmCycleCounter<"unhalted_core_cycles">;
def SKXPort0Counter : PfmIssueCounter<SKXPort0, ["uops_dispatched_port:port_0"]>;
def SKXPort1Counter : PfmIssueCounter<SKXPort1, ["uops_dispatched_port:port_1"]>;
def SKXPort2Counter : PfmIssueCounter<SKXPort2, ["uops_dispatched_port:port_2"]>;
def SKXPort3Counter : PfmIssueCounter<SKXPort3, ["uops_dispatched_port:port_3"]>;
def SKXPort4Counter : PfmIssueCounter<SKXPort4, ["uops_dispatched_port:port_4"]>;
def SKXPort5Counter : PfmIssueCounter<SKXPort5, ["uops_dispatched_port:port_5"]>;
def SKXPort6Counter : PfmIssueCounter<SKXPort6, ["uops_dispatched_port:port_6"]>;
def SKXPort7Counter : PfmIssueCounter<SKXPort7, ["uops_dispatched_port:port_7"]>;
}

View File

@ -76,10 +76,15 @@ LatencyBenchmarkRunner::runMeasurements(const LLVMState &State,
// measure several times and take the minimum value.
constexpr const int NumMeasurements = 30;
int64_t MinLatency = std::numeric_limits<int64_t>::max();
// FIXME: Read the perf event from the MCSchedModel (see PR36984).
const pfm::PerfEvent CyclesPerfEvent("UNHALTED_CORE_CYCLES");
const char *CounterName = State.getSubtargetInfo()
.getSchedModel()
.getExtraProcessorInfo()
.PfmCounters.CycleCounter;
if (!CounterName)
llvm::report_fatal_error("sched model does not define a cycle counter");
const pfm::PerfEvent CyclesPerfEvent(CounterName);
if (!CyclesPerfEvent.valid())
llvm::report_fatal_error("invalid perf event 'UNHALTED_CORE_CYCLES'");
llvm::report_fatal_error("invalid perf event");
for (size_t I = 0; I < NumMeasurements; ++I) {
pfm::Counter Counter(CyclesPerfEvent);
Counter.start();

View File

@ -38,43 +38,6 @@ static llvm::Error makeError(llvm::Twine Msg) {
llvm::inconvertibleErrorCode());
}
// FIXME: Read the counter names from the ProcResourceUnits when PR36984 is
// fixed.
static const std::string *getEventNameFromProcResName(const char *ProcResName) {
static const std::unordered_map<std::string, std::string> Entries = {
{"SBPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
{"SBPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
{"SBPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
{"SBPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
{"HWPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
{"HWPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
{"HWPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
{"HWPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
{"HWPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
{"HWPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
{"HWPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
{"HWPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
{"SKLPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
{"SKLPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
{"SKLPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
{"SKLPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
{"SKLPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
{"SKLPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
{"SKLPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
{"SKXPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
{"SKXPort0", "UOPS_DISPATCHED_PORT:PORT_0"},
{"SKXPort1", "UOPS_DISPATCHED_PORT:PORT_1"},
{"SKXPort2", "UOPS_DISPATCHED_PORT:PORT_2"},
{"SKXPort3", "UOPS_DISPATCHED_PORT:PORT_3"},
{"SKXPort4", "UOPS_DISPATCHED_PORT:PORT_4"},
{"SKXPort5", "UOPS_DISPATCHED_PORT:PORT_5"},
{"SKXPort6", "UOPS_DISPATCHED_PORT:PORT_6"},
{"SKXPort7", "UOPS_DISPATCHED_PORT:PORT_7"},
};
const auto It = Entries.find(ProcResName);
return It == Entries.end() ? nullptr : &It->second;
}
static std::vector<llvm::MCInst> generateIndependentAssignments(
const LLVMState &State, const llvm::MCInstrDesc &InstrDesc,
llvm::SmallVector<Variable, 8> Vars, int MaxAssignments) {
@ -228,19 +191,19 @@ UopsBenchmarkRunner::runMeasurements(const LLVMState &State,
std::vector<BenchmarkMeasure> Result;
for (unsigned ProcResIdx = 1;
ProcResIdx < SchedModel.getNumProcResourceKinds(); ++ProcResIdx) {
const llvm::MCProcResourceDesc &ProcRes =
*SchedModel.getProcResource(ProcResIdx);
const std::string *const EventName =
getEventNameFromProcResName(ProcRes.Name);
if (!EventName)
const char *const PfmCounters = SchedModel.getExtraProcessorInfo()
.PfmCounters.IssueCounters[ProcResIdx];
if (!PfmCounters)
continue;
pfm::Counter Counter{pfm::PerfEvent(*EventName)};
// FIXME: Sum results when there are several counters for a single ProcRes
// (e.g. P23 on SandyBridge).
pfm::Counter Counter{pfm::PerfEvent(PfmCounters)};
Counter.start();
Function();
Counter.stop();
Result.push_back({llvm::itostr(ProcResIdx),
static_cast<double>(Counter.read()) / NumRepetitions,
ProcRes.Name});
SchedModel.getProcResource(ProcResIdx)->Name});
}
return Result;
}

View File

@ -239,6 +239,11 @@ void CodeGenSchedModels::collectOptionalProcessorInfo() {
// Collect processor RetireControlUnit descriptors if available.
collectRetireControlUnits();
// Find pfm counter definitions for each processor.
collectPfmCounters();
checkCompleteness();
}
/// Gather all processor models.
@ -1537,6 +1542,23 @@ void CodeGenSchedModels::collectRegisterFiles() {
}
}
// Collect all the RegisterFile definitions available in this target.
void CodeGenSchedModels::collectPfmCounters() {
for (Record *Def : Records.getAllDerivedDefinitions("PfmIssueCounter")) {
CodeGenProcModel &PM = getProcModel(Def->getValueAsDef("SchedModel"));
PM.PfmIssueCounterDefs.emplace_back(Def);
}
for (Record *Def : Records.getAllDerivedDefinitions("PfmCycleCounter")) {
CodeGenProcModel &PM = getProcModel(Def->getValueAsDef("SchedModel"));
if (PM.PfmCycleCounterDef) {
PrintFatalError(Def->getLoc(),
"multiple cycle counters for " +
Def->getValueAsDef("SchedModel")->getName());
}
PM.PfmCycleCounterDef = Def;
}
}
// Collect and sort WriteRes, ReadAdvance, and ProcResources.
void CodeGenSchedModels::collectProcResources() {
ProcResourceDefs = Records.getAllDerivedDefinitions("ProcResourceUnits");

View File

@ -238,6 +238,10 @@ struct CodeGenProcModel {
// Optional Retire Control Unit definition.
Record *RetireControlUnit;
// List of PfmCounters.
RecVec PfmIssueCounterDefs;
Record *PfmCycleCounterDef = nullptr;
CodeGenProcModel(unsigned Idx, std::string Name, Record *MDef,
Record *IDef) :
Index(Idx), ModelName(std::move(Name)), ModelDef(MDef), ItinsDef(IDef),
@ -252,7 +256,9 @@ struct CodeGenProcModel {
}
bool hasExtraProcessorInfo() const {
return RetireControlUnit || !RegisterFiles.empty();
return RetireControlUnit || !RegisterFiles.empty() ||
!PfmIssueCounterDefs.empty() ||
PfmCycleCounterDef != nullptr;
}
unsigned getProcResourceIdx(Record *PRDef) const;
@ -444,6 +450,8 @@ private:
void collectRegisterFiles();
void collectPfmCounters();
void collectOptionalProcessorInfo();
std::string createSchedClassName(Record *ItinClassDef,

View File

@ -635,7 +635,7 @@ static void EmitRegisterFileInfo(const CodeGenProcModel &ProcModel,
OS << ProcModel.ModelName << "RegisterCosts,\n ";
else
OS << "nullptr,\n ";
OS << NumCostEntries << " // Number of register cost entries.\n";
OS << NumCostEntries << ", // Number of register cost entries.\n";
}
unsigned
@ -686,6 +686,57 @@ SubtargetEmitter::EmitRegisterFileTables(const CodeGenProcModel &ProcModel,
return CostTblIndex;
}
static bool EmitPfmIssueCountersTable(const CodeGenProcModel &ProcModel,
raw_ostream &OS) {
std::vector<const Record *> CounterDefs(ProcModel.ProcResourceDefs.size());
bool HasCounters = false;
for (const Record *CounterDef : ProcModel.PfmIssueCounterDefs) {
const Record *&CD = CounterDefs[ProcModel.getProcResourceIdx(
CounterDef->getValueAsDef("Resource"))];
if (CD) {
PrintFatalError(CounterDef->getLoc(),
"multiple issue counters for " +
CounterDef->getValueAsDef("Resource")->getName());
}
CD = CounterDef;
HasCounters = true;
}
if (!HasCounters) {
return false;
}
OS << "\nstatic const char* " << ProcModel.ModelName
<< "PfmIssueCounters[] = {\n";
for (const Record *CounterDef : CounterDefs) {
if (CounterDef) {
const auto PfmCounters = CounterDef->getValueAsListOfStrings("Counters");
if (PfmCounters.empty())
PrintFatalError(CounterDef->getLoc(), "empty counter list");
for (const StringRef CounterName : PfmCounters)
OS << " \"" << CounterName << ",\"";
OS << ", //" << CounterDef->getValueAsDef("Resource")->getName() << "\n";
} else {
OS << " nullptr,\n";
}
}
OS << "};\n";
return true;
}
static void EmitPfmCounters(const CodeGenProcModel &ProcModel,
const bool HasPfmIssueCounters, raw_ostream &OS) {
// Emit the cycle counter.
if (ProcModel.PfmCycleCounterDef)
OS << " \"" << ProcModel.PfmCycleCounterDef->getValueAsString("Counter")
<< "\", // Cycle counter.\n";
else
OS << " nullptr, // No cycle counter.\n";
// Emit a reference to issue counters table.
if (HasPfmIssueCounters)
OS << " " << ProcModel.ModelName << "PfmIssueCounters\n";
else
OS << " nullptr, // No issue counters.\n";
}
void SubtargetEmitter::EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel,
raw_ostream &OS) {
@ -693,6 +744,9 @@ void SubtargetEmitter::EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel,
// defined register file), and a table of register costs.
unsigned NumCostEntries = EmitRegisterFileTables(ProcModel, OS);
// Generate a table of ProcRes counter names.
const bool HasPfmIssueCounters = EmitPfmIssueCountersTable(ProcModel, OS);
// Now generate a table for the extra processor info.
OS << "\nstatic const llvm::MCExtraProcessorInfo " << ProcModel.ModelName
<< "ExtraInfo = {\n ";
@ -705,6 +759,8 @@ void SubtargetEmitter::EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel,
EmitRegisterFileInfo(ProcModel, ProcModel.RegisterFiles.size(),
NumCostEntries, OS);
EmitPfmCounters(ProcModel, HasPfmIssueCounters, OS);
OS << "};\n";
}
@ -1308,9 +1364,9 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) {
else
OS << " nullptr, // No Itinerary\n";
if (PM.hasExtraProcessorInfo())
OS << " &" << PM.ModelName << "ExtraInfo\n";
OS << " &" << PM.ModelName << "ExtraInfo,\n";
else
OS << " nullptr // No extra processor descriptor\n";
OS << " nullptr // No extra processor descriptor\n";
OS << "};\n";
}
}