1//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "Target.h"
9
10#include "LatencyBenchmarkRunner.h"
11#include "ParallelSnippetGenerator.h"
12#include "PerfHelper.h"
13#include "SerialSnippetGenerator.h"
14#include "UopsBenchmarkRunner.h"
15#include "llvm/ADT/Twine.h"
16#include "llvm/Support/Error.h"
17#include "llvm/TargetParser/SubtargetFeature.h"
18
19namespace llvm {
20namespace exegesis {
21
22cl::OptionCategory Options("llvm-exegesis options");
23cl::OptionCategory BenchmarkOptions("llvm-exegesis benchmark options");
24cl::OptionCategory AnalysisOptions("llvm-exegesis analysis options");
25
26ExegesisTarget::~ExegesisTarget() {} // anchor.
27
28static ExegesisTarget *FirstTarget = nullptr;
29
30const ExegesisTarget *ExegesisTarget::lookup(Triple TT) {
31 for (const ExegesisTarget *T = FirstTarget; T != nullptr; T = T->Next) {
32 if (T->matchesArch(Arch: TT.getArch()))
33 return T;
34 }
35 return nullptr;
36}
37
38const char *
39ExegesisTarget::getIgnoredOpcodeReasonOrNull(const LLVMState &State,
40 unsigned Opcode) const {
41 const MCInstrDesc &InstrDesc = State.getIC().getInstr(Opcode).Description;
42 if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook())
43 return "Unsupported opcode: isPseudo/usesCustomInserter";
44 if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
45 return "Unsupported opcode: isBranch/isIndirectBranch";
46 if (InstrDesc.isCall() || InstrDesc.isReturn())
47 return "Unsupported opcode: isCall/isReturn";
48 if (InstrDesc.getSchedClass() == 0)
49 return "Unsupported opcode: No Sched Class";
50 return nullptr;
51}
52
53Expected<std::unique_ptr<pfm::CounterGroup>>
54ExegesisTarget::createCounter(StringRef CounterName, const LLVMState &,
55 ArrayRef<const char *> ValidationCounters,
56 const pid_t ProcessID) const {
57 pfm::PerfEvent Event(CounterName);
58 if (!Event.valid())
59 return make_error<Failure>(Args: Twine("Unable to create counter with name '")
60 .concat(Suffix: CounterName)
61 .concat(Suffix: "'"));
62
63 std::vector<pfm::PerfEvent> ValidationEvents;
64 for (const char *ValCounterName : ValidationCounters) {
65 ValidationEvents.emplace_back(args&: ValCounterName);
66 if (!ValidationEvents.back().valid())
67 return make_error<Failure>(
68 Args: Twine("Unable to create validation counter with name '")
69 .concat(Suffix: ValCounterName)
70 .concat(Suffix: "'"));
71 }
72
73 return std::make_unique<pfm::CounterGroup>(
74 args: std::move(Event), args: std::move(ValidationEvents), args: ProcessID);
75}
76
77void ExegesisTarget::registerTarget(ExegesisTarget *Target) {
78 if (FirstTarget == nullptr) {
79 FirstTarget = Target;
80 return;
81 }
82 if (Target->Next != nullptr)
83 return; // Already registered.
84 Target->Next = FirstTarget;
85 FirstTarget = Target;
86}
87
88std::unique_ptr<SnippetGenerator> ExegesisTarget::createSnippetGenerator(
89 Benchmark::ModeE Mode, const LLVMState &State,
90 const SnippetGenerator::Options &Opts) const {
91 switch (Mode) {
92 case Benchmark::Unknown:
93 return nullptr;
94 case Benchmark::Latency:
95 return createSerialSnippetGenerator(State, Opts);
96 case Benchmark::Uops:
97 case Benchmark::InverseThroughput:
98 return createParallelSnippetGenerator(State, Opts);
99 }
100 return nullptr;
101}
102
103Expected<std::unique_ptr<BenchmarkRunner>>
104ExegesisTarget::createBenchmarkRunner(
105 Benchmark::ModeE Mode, const LLVMState &State,
106 BenchmarkPhaseSelectorE BenchmarkPhaseSelector,
107 BenchmarkRunner::ExecutionModeE ExecutionMode,
108 unsigned BenchmarkRepeatCount, ArrayRef<ValidationEvent> ValidationCounters,
109 Benchmark::ResultAggregationModeE ResultAggMode) const {
110 PfmCountersInfo PfmCounters = State.getPfmCounters();
111 switch (Mode) {
112 case Benchmark::Unknown:
113 return nullptr;
114 case Benchmark::Latency:
115 case Benchmark::InverseThroughput:
116 if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
117 !PfmCounters.CycleCounter) {
118 const char *ModeName = Mode == Benchmark::Latency
119 ? "latency"
120 : "inverse_throughput";
121 return make_error<Failure>(
122 Args: Twine("can't run '")
123 .concat(Suffix: ModeName)
124 .concat(
125 Suffix: "' mode, sched model does not define a cycle counter. You "
126 "can pass --benchmark-phase=... to skip the actual "
127 "benchmarking or --use-dummy-perf-counters to not query "
128 "the kernel for real event counts."));
129 }
130 return createLatencyBenchmarkRunner(
131 State, Mode, BenchmarkPhaseSelector, ResultAggMode, ExecutionMode,
132 ValidationCounters, BenchmarkRepeatCount);
133 case Benchmark::Uops:
134 if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
135 !PfmCounters.UopsCounter && !PfmCounters.IssueCounters)
136 return make_error<Failure>(
137 Args: "can't run 'uops' mode, sched model does not define uops or issue "
138 "counters. You can pass --benchmark-phase=... to skip the actual "
139 "benchmarking or --use-dummy-perf-counters to not query the kernel "
140 "for real event counts.");
141 return createUopsBenchmarkRunner(State, BenchmarkPhaseSelector,
142 ResultAggMode, ExecutionMode,
143 ValidationCounters);
144 }
145 return nullptr;
146}
147
148std::unique_ptr<SnippetGenerator> ExegesisTarget::createSerialSnippetGenerator(
149 const LLVMState &State, const SnippetGenerator::Options &Opts) const {
150 return std::make_unique<SerialSnippetGenerator>(args: State, args: Opts);
151}
152
153std::unique_ptr<SnippetGenerator> ExegesisTarget::createParallelSnippetGenerator(
154 const LLVMState &State, const SnippetGenerator::Options &Opts) const {
155 return std::make_unique<ParallelSnippetGenerator>(args: State, args: Opts);
156}
157
158std::unique_ptr<BenchmarkRunner> ExegesisTarget::createLatencyBenchmarkRunner(
159 const LLVMState &State, Benchmark::ModeE Mode,
160 BenchmarkPhaseSelectorE BenchmarkPhaseSelector,
161 Benchmark::ResultAggregationModeE ResultAggMode,
162 BenchmarkRunner::ExecutionModeE ExecutionMode,
163 ArrayRef<ValidationEvent> ValidationCounters,
164 unsigned BenchmarkRepeatCount) const {
165 return std::make_unique<LatencyBenchmarkRunner>(
166 args: State, args&: Mode, args&: BenchmarkPhaseSelector, args&: ResultAggMode, args&: ExecutionMode,
167 args&: ValidationCounters, args&: BenchmarkRepeatCount);
168}
169
170std::unique_ptr<BenchmarkRunner> ExegesisTarget::createUopsBenchmarkRunner(
171 const LLVMState &State, BenchmarkPhaseSelectorE BenchmarkPhaseSelector,
172 Benchmark::ResultAggregationModeE /*unused*/,
173 BenchmarkRunner::ExecutionModeE ExecutionMode,
174 ArrayRef<ValidationEvent> ValidationCounters) const {
175 return std::make_unique<UopsBenchmarkRunner>(
176 args: State, args&: BenchmarkPhaseSelector, args&: ExecutionMode, args&: ValidationCounters);
177}
178
179static_assert(std::is_trivial_v<PfmCountersInfo>,
180 "We shouldn't have dynamic initialization here");
181
182const PfmCountersInfo PfmCountersInfo::Default = {.CycleCounter: nullptr, .UopsCounter: nullptr, .IssueCounters: nullptr,
183 .NumIssueCounters: 0u, .ValidationEvents: nullptr, .NumValidationEvents: 0u};
184const PfmCountersInfo PfmCountersInfo::Dummy = {
185 .CycleCounter: pfm::PerfEvent::DummyEventString,
186 .UopsCounter: pfm::PerfEvent::DummyEventString,
187 .IssueCounters: nullptr,
188 .NumIssueCounters: 0u,
189 .ValidationEvents: nullptr,
190 .NumValidationEvents: 0u};
191
192const PfmCountersInfo &ExegesisTarget::getPfmCounters(StringRef CpuName) const {
193 assert(
194 is_sorted(CpuPfmCounters,
195 [](const CpuAndPfmCounters &LHS, const CpuAndPfmCounters &RHS) {
196 return strcmp(LHS.CpuName, RHS.CpuName) < 0;
197 }) &&
198 "CpuPfmCounters table is not sorted");
199
200 // Find entry
201 auto Found = lower_bound(Range: CpuPfmCounters, Value&: CpuName);
202 if (Found == CpuPfmCounters.end() || StringRef(Found->CpuName) != CpuName) {
203 // Use the default.
204 if (!CpuPfmCounters.empty() && CpuPfmCounters.begin()->CpuName[0] == '\0') {
205 Found = CpuPfmCounters.begin(); // The target specifies a default.
206 } else {
207 return PfmCountersInfo::Default; // No default for the target.
208 }
209 }
210 assert(Found->PCI && "Missing counters");
211 return *Found->PCI;
212}
213
214const PfmCountersInfo &ExegesisTarget::getDummyPfmCounters() const {
215 return PfmCountersInfo::Dummy;
216}
217
218ExegesisTarget::SavedState::~SavedState() {} // anchor.
219
220namespace {
221
222bool opcodeIsNotAvailable(unsigned, const FeatureBitset &) { return false; }
223
224// Default implementation.
225class ExegesisDefaultTarget : public ExegesisTarget {
226public:
227 ExegesisDefaultTarget() : ExegesisTarget({}, opcodeIsNotAvailable) {}
228
229private:
230 std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg,
231 const APInt &Value) const override {
232 llvm_unreachable("Not yet implemented");
233 }
234
235 bool matchesArch(Triple::ArchType Arch) const override {
236 llvm_unreachable("never called");
237 return false;
238 }
239};
240
241} // namespace
242
243const ExegesisTarget &ExegesisTarget::getDefault() {
244 static ExegesisDefaultTarget Target;
245 return Target;
246}
247
248} // namespace exegesis
249} // namespace llvm
250