llvm.org GIT mirror llvm / 6bd5429
[NFC][llvm-exegesis] Refactor ResolvedSchedClass & friends Summary: `ResolvedSchedClass` will need to be used outside of `Analysis` (before `InstructionBenchmarkClustering` even), therefore promote it into a non-private top-level class, and while there also move all of the functions that are only called by `ResolvedSchedClass` into that same new file. Reviewers: courbet, gchatelet Reviewed By: courbet Subscribers: mgorny, tschuett, mgrang, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59993 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@357259 91177308-0d34-0410-b5e6-96231b3b80d8 Roman Lebedev 1 year, 7 months ago
8 changed file(s) with 427 addition(s) and 351 deletion(s). Raw diff Collapse all Expand all
1818 namespace exegesis {
1919
2020 static const char kCsvSep = ',';
21
22 static unsigned resolveSchedClassId(const llvm::MCSubtargetInfo &STI,
23 unsigned SchedClassId,
24 const llvm::MCInst &MCI) {
25 const auto &SM = STI.getSchedModel();
26 while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant())
27 SchedClassId =
28 STI.resolveVariantSchedClass(SchedClassId, &MCI, SM.getProcessorID());
29 return SchedClassId;
30 }
3121
3222 namespace {
3323
149139 OS << kCsvSep;
150140 assert(!Point.Key.Instructions.empty());
151141 const llvm::MCInst &MCI = Point.keyInstruction();
152 const unsigned SchedClassId = resolveSchedClassId(
153 *SubtargetInfo_, InstrInfo_->get(MCI.getOpcode()).getSchedClass(), MCI);
154
142 unsigned SchedClassId;
143 std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
144 *SubtargetInfo_, *InstrInfo_, MCI);
155145 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
156146 const llvm::MCSchedClassDesc *const SCDesc =
157147 SubtargetInfo_->getSchedModel().getSchedClassDesc(SchedClassId);
238228 // FIXME: we should be using the tuple of classes for instructions in the
239229 // snippet as key.
240230 const llvm::MCInst &MCI = Point.keyInstruction();
241 unsigned SchedClassId = InstrInfo_->get(MCI.getOpcode()).getSchedClass();
242 const bool WasVariant = SchedClassId && SubtargetInfo_->getSchedModel()
243 .getSchedClassDesc(SchedClassId)
244 ->isVariant();
245 SchedClassId = resolveSchedClassId(*SubtargetInfo_, SchedClassId, MCI);
231 unsigned SchedClassId;
232 bool WasVariant;
233 std::tie(SchedClassId, WasVariant) =
234 ResolvedSchedClass::resolveSchedClassId(*SubtargetInfo_, *InstrInfo_,
235 MCI);
246236 const auto IndexIt = SchedClassIdToIndex.find(SchedClassId);
247237 if (IndexIt == SchedClassIdToIndex.end()) {
248238 // Create a new entry.
346336 OS << "
"; 347337 } 348338 349 // Return the non-redundant list of WriteProcRes used by the given sched class. 350 // The scheduling model for LLVM is such that each instruction has a certain 351 // number of uops which consume resources which are described by WriteProcRes 352 // entries. Each entry describe how many cycles are spent on a specific ProcRes 353 // kind. 354 // For example, an instruction might have 3 uOps, one dispatching on P0 355 // (ProcResIdx=1) and two on P06 (ProcResIdx = 7). 356 // Note that LLVM additionally denormalizes resource consumption to include 357 // usage of super resources by subresources. So in practice if there exists a 358 // P016 (ProcResIdx=10), then the cycles consumed by P0 are also consumed by 359 // P06 (ProcResIdx = 7) and P016 (ProcResIdx = 10), and the resources consumed 360 // by P06 are also consumed by P016. In the figure below, parenthesized cycles 361 // denote implied usage of superresources by subresources: 362 // P0 P06 P016 363 // uOp1 1 (1) (1) 364 // uOp2 1 (1) 365 // uOp3 1 (1) 366 // ============================= 367 // 1 3 3 368 // Eventually we end up with three entries for the WriteProcRes of the 369 // instruction: 370 // {ProcResIdx=1, Cycles=1} // P0 371 // {ProcResIdx=7, Cycles=3} // P06 372 // {ProcResIdx=10, Cycles=3} // P016 373 // 374 // Note that in this case, P016 does not contribute any cycles, so it would 375 // be removed by this function. 376 // FIXME: Move this to MCSubtargetInfo and use it in llvm-mca. 377 static llvm::SmallVector 378 getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc, 379 const llvm::MCSubtargetInfo &STI) { 380 llvm::SmallVector Result; 381 const auto &SM = STI.getSchedModel(); 382 const unsigned NumProcRes = SM.getNumProcResourceKinds(); 383 384 // This assumes that the ProcResDescs are sorted in topological order, which 385 // is guaranteed by the tablegen backend. 386 llvm::SmallVector ProcResUnitUsage(NumProcRes); 387 for (const auto *WPR = STI.getWriteProcResBegin(&SCDesc), 388 *const WPREnd = STI.getWriteProcResEnd(&SCDesc); 389 WPR != WPREnd; ++WPR) { 390 const llvm::MCProcResourceDesc *const ProcResDesc = 391 SM.getProcResource(WPR->ProcResourceIdx); 392 if (ProcResDesc->SubUnitsIdxBegin == nullptr) { 393 // This is a ProcResUnit. 394 Result.push_back({WPR->ProcResourceIdx, WPR->Cycles}); 395 ProcResUnitUsage[WPR->ProcResourceIdx] += WPR->Cycles; 396 } else { 397 // This is a ProcResGroup. First see if it contributes any cycles or if 398 // it has cycles just from subunits. 399 float RemainingCycles = WPR->Cycles; 400 for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin; 401 SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits; 402 ++SubResIdx) { 403 RemainingCycles -= ProcResUnitUsage[*SubResIdx]; 404 } 405 if (RemainingCycles < 0.01f) { 406 // The ProcResGroup contributes no cycles of its own. 407 continue; 408 } 409 // The ProcResGroup contributes `RemainingCycles` cycles of its own. 410 Result.push_back({WPR->ProcResourceIdx, 411 static_cast(std::round(RemainingCycles))}); 412 // Spread the remaining cycles over all subunits. 413 for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin; 414 SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits; 415 ++SubResIdx) { 416 ProcResUnitUsage[*SubResIdx] += RemainingCycles / ProcResDesc->NumUnits; 417 } 418 } 419 } 420 return Result; 421 } 422 423 Analysis::ResolvedSchedClass::ResolvedSchedClass( 424 const llvm::MCSubtargetInfo &STI, unsigned ResolvedSchedClassId, 425 bool WasVariant) 426 : SchedClassId(ResolvedSchedClassId), SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)), 427 WasVariant(WasVariant), 428 NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)), 429 IdealizedProcResPressure(computeIdealizedProcResPressure( 430 STI.getSchedModel(), NonRedundantWriteProcRes)) { 431 assert((SCDesc == nullptr || !SCDesc->isVariant()) && 432 "ResolvedSchedClass should never be variant"); 433 } 434 435339 void Analysis::SchedClassCluster::addPoint( 436340 size_t PointId, const InstructionBenchmarkClustering &Clustering) { 437341 PointIds.push_back(PointId); 736640 return llvm::Error::success(); 737641 } 738642 739 // Distributes a pressure budget as evenly as possible on the provided subunits 740 // given the already existing port pressure distribution. 741 // 742 // The algorithm is as follows: while there is remaining pressure to 743 // distribute, find the subunits with minimal pressure, and distribute 744 // remaining pressure equally up to the pressure of the unit with 745 // second-to-minimal pressure. 746 // For example, let's assume we want to distribute 2*P1256 747 // (Subunits = [P1,P2,P5,P6]), and the starting DensePressure is: 748 // DensePressure = P0 P1 P2 P3 P4 P5 P6 P7 749 // 0.1 0.3 0.2 0.0 0.0 0.5 0.5 0.5 750 // RemainingPressure = 2.0 751 // We sort the subunits by pressure: 752 // Subunits = [(P2,p=0.2), (P1,p=0.3), (P5,p=0.5), (P6, p=0.5)] 753 // We'll first start by the subunits with minimal pressure, which are at 754 // the beginning of the sorted array. In this example there is one (P2). 755 // The subunit with second-to-minimal pressure is the next one in the 756 // array (P1). So we distribute 0.1 pressure to P2, and remove 0.1 cycles 757 // from the budget. 758 // Subunits = [(P2,p=0.3), (P1,p=0.3), (P5,p=0.5), (P5,p=0.5)] 759 // RemainingPressure = 1.9 760 // We repeat this process: distribute 0.2 pressure on each of the minimal 761 // P2 and P1, decrease budget by 2*0.2: 762 // Subunits = [(P2,p=0.5), (P1,p=0.5), (P5,p=0.5), (P5,p=0.5)] 763 // RemainingPressure = 1.5 764 // There are no second-to-minimal subunits so we just share the remaining 765 // budget (1.5 cycles) equally: 766 // Subunits = [(P2,p=0.875), (P1,p=0.875), (P5,p=0.875), (P5,p=0.875)] 767 // RemainingPressure = 0.0 768 // We stop as there is no remaining budget to distribute. 769 void distributePressure(float RemainingPressure, 770 llvm::SmallVector Subunits, 771 llvm::SmallVector &DensePressure) { 772 // Find the number of subunits with minimal pressure (they are at the 773 // front). 774 llvm::sort(Subunits, [&DensePressure](const uint16_t A, const uint16_t B) { 775 return DensePressure[A] < DensePressure[B]; 776 }); 777 const auto getPressureForSubunit = [&DensePressure, 778 &Subunits](size_t I) -> float & { 779 return DensePressure[Subunits[I]]; 780 }; 781 size_t NumMinimalSU = 1; 782 while (NumMinimalSU < Subunits.size() && 783 getPressureForSubunit(NumMinimalSU) == getPressureForSubunit(0)) { 784 ++NumMinimalSU; 785 } 786 while (RemainingPressure > 0.0f) { 787 if (NumMinimalSU == Subunits.size()) { 788 // All units are minimal, just distribute evenly and be done. 789 for (size_t I = 0; I < NumMinimalSU; ++I) { 790 getPressureForSubunit(I) += RemainingPressure / NumMinimalSU; 791 } 792 return; 793 } 794 // Distribute the remaining pressure equally. 795 const float MinimalPressure = getPressureForSubunit(NumMinimalSU - 1); 796 const float SecondToMinimalPressure = getPressureForSubunit(NumMinimalSU); 797 assert(MinimalPressure < SecondToMinimalPressure); 798 const float Increment = SecondToMinimalPressure - MinimalPressure; 799 if (RemainingPressure <= NumMinimalSU * Increment) { 800 // There is not enough remaining pressure. 801 for (size_t I = 0; I < NumMinimalSU; ++I) { 802 getPressureForSubunit(I) += RemainingPressure / NumMinimalSU; 803 } 804 return; 805 } 806 // Bump all minimal pressure subunits to `SecondToMinimalPressure`. 807 for (size_t I = 0; I < NumMinimalSU; ++I) { 808 getPressureForSubunit(I) = SecondToMinimalPressure; 809 RemainingPressure -= SecondToMinimalPressure; 810 } 811 while (NumMinimalSU < Subunits.size() && 812 getPressureForSubunit(NumMinimalSU) == SecondToMinimalPressure) { 813 ++NumMinimalSU; 814 } 815 } 816 } 817 818 std::vector> computeIdealizedProcResPressure( 819 const llvm::MCSchedModel &SM, 820 llvm::SmallVector WPRS) { 821 // DensePressure[I] is the port pressure for Proc Resource I. 822 llvm::SmallVector DensePressure(SM.getNumProcResourceKinds()); 823 llvm::sort(WPRS, [](const llvm::MCWriteProcResEntry &A, 824 const llvm::MCWriteProcResEntry &B) { 825 return A.ProcResourceIdx < B.ProcResourceIdx; 826 }); 827 for (const llvm::MCWriteProcResEntry &WPR : WPRS) { 828 // Get units for the entry. 829 const llvm::MCProcResourceDesc *const ProcResDesc = 830 SM.getProcResource(WPR.ProcResourceIdx); 831 if (ProcResDesc->SubUnitsIdxBegin == nullptr) { 832 // This is a ProcResUnit. 833 DensePressure[WPR.ProcResourceIdx] += WPR.Cycles; 834 } else { 835 // This is a ProcResGroup. 836 llvm::SmallVector Subunits(ProcResDesc->SubUnitsIdxBegin, 837 ProcResDesc->SubUnitsIdxBegin + 838 ProcResDesc->NumUnits); 839 distributePressure(WPR.Cycles, Subunits, DensePressure); 840 } 841 } 842 // Turn dense pressure into sparse pressure by removing zero entries. 843 std::vector> Pressure; 844 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { 845 if (DensePressure[I] > 0.0f) 846 Pressure.emplace_back(I, DensePressure[I]); 847 } 848 return Pressure; 849 } 850 851643 } // namespace exegesis 852644 } // namespace llvm
1414 #define LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H
1515
1616 #include "Clustering.h"
17 #include "SchedClassResolution.h"
1718 #include "llvm/MC/MCContext.h"
1819 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
1920 #include "llvm/MC/MCInstPrinter.h"
4950
5051 private:
5152 using ClusterId = InstructionBenchmarkClustering::ClusterId;
52
53 // An llvm::MCSchedClassDesc augmented with some additional data.
54 struct ResolvedSchedClass {
55 ResolvedSchedClass(const llvm::MCSubtargetInfo &STI,
56 unsigned ResolvedSchedClassId, bool WasVariant);
57
58 const unsigned SchedClassId;
59 const llvm::MCSchedClassDesc *const SCDesc;
60 const bool WasVariant; // Whether the original class was variant.
61 const llvm::SmallVector
62 NonRedundantWriteProcRes;
63 const std::vector> IdealizedProcResPressure;
64 };
6553
6654 // Represents the intersection of a sched class and a cluster.
6755 class SchedClassCluster {
136124 const bool AnalysisDisplayUnstableOpcodes_;
137125 };
138126
139 // Computes the idealized ProcRes Unit pressure. This is the expected
140 // distribution if the CPU scheduler can distribute the load as evenly as
141 // possible.
142 std::vector> computeIdealizedProcResPressure(
143 const llvm::MCSchedModel &SM,
144 llvm::SmallVector WPRS);
145
146127 } // namespace exegesis
147128 } // namespace llvm
148129
2727 MCInstrDescView.cpp
2828 PerfHelper.cpp
2929 RegisterAliasing.cpp
30 RegisterValue.cpp
31 SchedClassResolution.cpp
3032 SnippetGenerator.cpp
31 RegisterValue.cpp
3233 Target.cpp
3334 Uops.cpp
3435 )
0 //===-- SchedClassResolution.cpp --------------------------------*- C++ -*-===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7
8 #include "SchedClassResolution.h"
9 #include "BenchmarkResult.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/MC/MCAsmInfo.h"
12 #include "llvm/Support/FormatVariadic.h"
13 #include
14 #include
15 #include
16
17 namespace llvm {
18 namespace exegesis {
19
20 // Return the non-redundant list of WriteProcRes used by the given sched class.
21 // The scheduling model for LLVM is such that each instruction has a certain
22 // number of uops which consume resources which are described by WriteProcRes
23 // entries. Each entry describe how many cycles are spent on a specific ProcRes
24 // kind.
25 // For example, an instruction might have 3 uOps, one dispatching on P0
26 // (ProcResIdx=1) and two on P06 (ProcResIdx = 7).
27 // Note that LLVM additionally denormalizes resource consumption to include
28 // usage of super resources by subresources. So in practice if there exists a
29 // P016 (ProcResIdx=10), then the cycles consumed by P0 are also consumed by
30 // P06 (ProcResIdx = 7) and P016 (ProcResIdx = 10), and the resources consumed
31 // by P06 are also consumed by P016. In the figure below, parenthesized cycles
32 // denote implied usage of superresources by subresources:
33 // P0 P06 P016
34 // uOp1 1 (1) (1)
35 // uOp2 1 (1)
36 // uOp3 1 (1)
37 // =============================
38 // 1 3 3
39 // Eventually we end up with three entries for the WriteProcRes of the
40 // instruction:
41 // {ProcResIdx=1, Cycles=1} // P0
42 // {ProcResIdx=7, Cycles=3} // P06
43 // {ProcResIdx=10, Cycles=3} // P016
44 //
45 // Note that in this case, P016 does not contribute any cycles, so it would
46 // be removed by this function.
47 // FIXME: Move this to MCSubtargetInfo and use it in llvm-mca.
48 static llvm::SmallVector
49 getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc,
50 const llvm::MCSubtargetInfo &STI) {
51 llvm::SmallVector Result;
52 const auto &SM = STI.getSchedModel();
53 const unsigned NumProcRes = SM.getNumProcResourceKinds();
54
55 // This assumes that the ProcResDescs are sorted in topological order, which
56 // is guaranteed by the tablegen backend.
57 llvm::SmallVector ProcResUnitUsage(NumProcRes);
58 for (const auto *WPR = STI.getWriteProcResBegin(&SCDesc),
59 *const WPREnd = STI.getWriteProcResEnd(&SCDesc);
60 WPR != WPREnd; ++WPR) {
61 const llvm::MCProcResourceDesc *const ProcResDesc =
62 SM.getProcResource(WPR->ProcResourceIdx);
63 if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
64 // This is a ProcResUnit.
65 Result.push_back({WPR->ProcResourceIdx, WPR->Cycles});
66 ProcResUnitUsage[WPR->ProcResourceIdx] += WPR->Cycles;
67 } else {
68 // This is a ProcResGroup. First see if it contributes any cycles or if
69 // it has cycles just from subunits.
70 float RemainingCycles = WPR->Cycles;
71 for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
72 SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
73 ++SubResIdx) {
74 RemainingCycles -= ProcResUnitUsage[*SubResIdx];
75 }
76 if (RemainingCycles < 0.01f) {
77 // The ProcResGroup contributes no cycles of its own.
78 continue;
79 }
80 // The ProcResGroup contributes `RemainingCycles` cycles of its own.
81 Result.push_back({WPR->ProcResourceIdx,
82 static_cast(std::round(RemainingCycles))});
83 // Spread the remaining cycles over all subunits.
84 for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
85 SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
86 ++SubResIdx) {
87 ProcResUnitUsage[*SubResIdx] += RemainingCycles / ProcResDesc->NumUnits;
88 }
89 }
90 }
91 return Result;
92 }
93
94 // Distributes a pressure budget as evenly as possible on the provided subunits
95 // given the already existing port pressure distribution.
96 //
97 // The algorithm is as follows: while there is remaining pressure to
98 // distribute, find the subunits with minimal pressure, and distribute
99 // remaining pressure equally up to the pressure of the unit with
100 // second-to-minimal pressure.
101 // For example, let's assume we want to distribute 2*P1256
102 // (Subunits = [P1,P2,P5,P6]), and the starting DensePressure is:
103 // DensePressure = P0 P1 P2 P3 P4 P5 P6 P7
104 // 0.1 0.3 0.2 0.0 0.0 0.5 0.5 0.5
105 // RemainingPressure = 2.0
106 // We sort the subunits by pressure:
107 // Subunits = [(P2,p=0.2), (P1,p=0.3), (P5,p=0.5), (P6, p=0.5)]
108 // We'll first start by the subunits with minimal pressure, which are at
109 // the beginning of the sorted array. In this example there is one (P2).
110 // The subunit with second-to-minimal pressure is the next one in the
111 // array (P1). So we distribute 0.1 pressure to P2, and remove 0.1 cycles
112 // from the budget.
113 // Subunits = [(P2,p=0.3), (P1,p=0.3), (P5,p=0.5), (P5,p=0.5)]
114 // RemainingPressure = 1.9
115 // We repeat this process: distribute 0.2 pressure on each of the minimal
116 // P2 and P1, decrease budget by 2*0.2:
117 // Subunits = [(P2,p=0.5), (P1,p=0.5), (P5,p=0.5), (P5,p=0.5)]
118 // RemainingPressure = 1.5
119 // There are no second-to-minimal subunits so we just share the remaining
120 // budget (1.5 cycles) equally:
121 // Subunits = [(P2,p=0.875), (P1,p=0.875), (P5,p=0.875), (P5,p=0.875)]
122 // RemainingPressure = 0.0
123 // We stop as there is no remaining budget to distribute.
124 static void distributePressure(float RemainingPressure,
125 llvm::SmallVector Subunits,
126 llvm::SmallVector &DensePressure) {
127 // Find the number of subunits with minimal pressure (they are at the
128 // front).
129 llvm::sort(Subunits, [&DensePressure](const uint16_t A, const uint16_t B) {
130 return DensePressure[A] < DensePressure[B];
131 });
132 const auto getPressureForSubunit = [&DensePressure,
133 &Subunits](size_t I) -> float & {
134 return DensePressure[Subunits[I]];
135 };
136 size_t NumMinimalSU = 1;
137 while (NumMinimalSU < Subunits.size() &&
138 getPressureForSubunit(NumMinimalSU) == getPressureForSubunit(0)) {
139 ++NumMinimalSU;
140 }
141 while (RemainingPressure > 0.0f) {
142 if (NumMinimalSU == Subunits.size()) {
143 // All units are minimal, just distribute evenly and be done.
144 for (size_t I = 0; I < NumMinimalSU; ++I) {
145 getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
146 }
147 return;
148 }
149 // Distribute the remaining pressure equally.
150 const float MinimalPressure = getPressureForSubunit(NumMinimalSU - 1);
151 const float SecondToMinimalPressure = getPressureForSubunit(NumMinimalSU);
152 assert(MinimalPressure < SecondToMinimalPressure);
153 const float Increment = SecondToMinimalPressure - MinimalPressure;
154 if (RemainingPressure <= NumMinimalSU * Increment) {
155 // There is not enough remaining pressure.
156 for (size_t I = 0; I < NumMinimalSU; ++I) {
157 getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
158 }
159 return;
160 }
161 // Bump all minimal pressure subunits to `SecondToMinimalPressure`.
162 for (size_t I = 0; I < NumMinimalSU; ++I) {
163 getPressureForSubunit(I) = SecondToMinimalPressure;
164 RemainingPressure -= SecondToMinimalPressure;
165 }
166 while (NumMinimalSU < Subunits.size() &&
167 getPressureForSubunit(NumMinimalSU) == SecondToMinimalPressure) {
168 ++NumMinimalSU;
169 }
170 }
171 }
172
173 std::vector> computeIdealizedProcResPressure(
174 const llvm::MCSchedModel &SM,
175 llvm::SmallVector WPRS) {
176 // DensePressure[I] is the port pressure for Proc Resource I.
177 llvm::SmallVector DensePressure(SM.getNumProcResourceKinds());
178 llvm::sort(WPRS, [](const llvm::MCWriteProcResEntry &A,
179 const llvm::MCWriteProcResEntry &B) {
180 return A.ProcResourceIdx < B.ProcResourceIdx;
181 });
182 for (const llvm::MCWriteProcResEntry &WPR : WPRS) {
183 // Get units for the entry.
184 const llvm::MCProcResourceDesc *const ProcResDesc =
185 SM.getProcResource(WPR.ProcResourceIdx);
186 if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
187 // This is a ProcResUnit.
188 DensePressure[WPR.ProcResourceIdx] += WPR.Cycles;
189 } else {
190 // This is a ProcResGroup.
191 llvm::SmallVector Subunits(ProcResDesc->SubUnitsIdxBegin,
192 ProcResDesc->SubUnitsIdxBegin +
193 ProcResDesc->NumUnits);
194 distributePressure(WPR.Cycles, Subunits, DensePressure);
195 }
196 }
197 // Turn dense pressure into sparse pressure by removing zero entries.
198 std::vector> Pressure;
199 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
200 if (DensePressure[I] > 0.0f)
201 Pressure.emplace_back(I, DensePressure[I]);
202 }
203 return Pressure;
204 }
205
206 ResolvedSchedClass::ResolvedSchedClass(const llvm::MCSubtargetInfo &STI,
207 unsigned ResolvedSchedClassId,
208 bool WasVariant)
209 : SchedClassId(ResolvedSchedClassId),
210 SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)),
211 WasVariant(WasVariant),
212 NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)),
213 IdealizedProcResPressure(computeIdealizedProcResPressure(
214 STI.getSchedModel(), NonRedundantWriteProcRes)) {
215 assert((SCDesc == nullptr || !SCDesc->isVariant()) &&
216 "ResolvedSchedClass should never be variant");
217 }
218
219 static unsigned ResolveVariantSchedClassId(const llvm::MCSubtargetInfo &STI,
220 unsigned SchedClassId,
221 const llvm::MCInst &MCI) {
222 const auto &SM = STI.getSchedModel();
223 while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant())
224 SchedClassId =
225 STI.resolveVariantSchedClass(SchedClassId, &MCI, SM.getProcessorID());
226 return SchedClassId;
227 }
228
229 std::pair
230 ResolvedSchedClass::resolveSchedClassId(
231 const llvm::MCSubtargetInfo &SubtargetInfo,
232 const llvm::MCInstrInfo &InstrInfo, const llvm::MCInst &MCI) {
233 unsigned SchedClassId = InstrInfo.get(MCI.getOpcode()).getSchedClass();
234 const bool WasVariant = SchedClassId && SubtargetInfo.getSchedModel()
235 .getSchedClassDesc(SchedClassId)
236 ->isVariant();
237 SchedClassId = ResolveVariantSchedClassId(SubtargetInfo, SchedClassId, MCI);
238 return std::make_pair(SchedClassId, WasVariant);
239 }
240
241 } // namespace exegesis
242 } // namespace llvm
0 //===-- SchedClassResolution.h ----------------------------------*- C++ -*-===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 ///
8 /// \file
9 /// Analysis output for benchmark results.
10 ///
11 //===----------------------------------------------------------------------===//
12
13 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
14 #define LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
15
16 #include "llvm/MC/MCContext.h"
17 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
18 #include "llvm/MC/MCInstPrinter.h"
19 #include "llvm/MC/MCInstrInfo.h"
20 #include "llvm/MC/MCObjectFileInfo.h"
21 #include "llvm/MC/MCSubtargetInfo.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/TargetRegistry.h"
24 #include "llvm/Support/raw_ostream.h"
25
26 namespace llvm {
27 namespace exegesis {
28
29 // Computes the idealized ProcRes Unit pressure. This is the expected
30 // distribution if the CPU scheduler can distribute the load as evenly as
31 // possible.
32 std::vector> computeIdealizedProcResPressure(
33 const llvm::MCSchedModel &SM,
34 llvm::SmallVector WPRS);
35
36 // An llvm::MCSchedClassDesc augmented with some additional data.
37 struct ResolvedSchedClass {
38 ResolvedSchedClass(const llvm::MCSubtargetInfo &STI,
39 unsigned ResolvedSchedClassId, bool WasVariant);
40
41 static std::pair
42 resolveSchedClassId(const llvm::MCSubtargetInfo &SubtargetInfo,
43 const llvm::MCInstrInfo &InstrInfo,
44 const llvm::MCInst &MCI);
45
46 const unsigned SchedClassId;
47 const llvm::MCSchedClassDesc *const SCDesc;
48 const bool WasVariant; // Whether the original class was variant.
49 const llvm::SmallVector
50 NonRedundantWriteProcRes;
51 const std::vector> IdealizedProcResPressure;
52 };
53
54 } // namespace exegesis
55 } // namespace llvm
56
57 #endif // LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
+0
-112
unittests/tools/llvm-exegesis/X86/AnalysisTest.cpp less more
None //===-- AnalysisTest.cpp ---------------------------------------*- C++ -*-===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7
8 #include "Analysis.h"
9
10 #include
11 #include
12
13 #include "llvm/Support/TargetRegistry.h"
14 #include "llvm/Support/TargetSelect.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace llvm {
19 namespace exegesis {
20 namespace {
21
22 using testing::Pair;
23 using testing::UnorderedElementsAre;
24
25 class AnalysisTest : public ::testing::Test {
26 protected:
27 AnalysisTest() {
28 const std::string TT = "x86_64-unknown-linux";
29 std::string error;
30 const llvm::Target *const TheTarget =
31 llvm::TargetRegistry::lookupTarget(TT, error);
32 if (!TheTarget) {
33 llvm::errs() << error << "\n";
34 return;
35 }
36 STI.reset(TheTarget->createMCSubtargetInfo(TT, "haswell", ""));
37
38 // Compute the ProxResIdx of ports uses in tests.
39 const auto &SM = STI->getSchedModel();
40 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
41 const std::string Name = SM.getProcResource(I)->Name;
42 if (Name == "HWPort0") {
43 P0Idx = I;
44 } else if (Name == "HWPort1") {
45 P1Idx = I;
46 } else if (Name == "HWPort5") {
47 P5Idx = I;
48 } else if (Name == "HWPort6") {
49 P6Idx = I;
50 } else if (Name == "HWPort05") {
51 P05Idx = I;
52 } else if (Name == "HWPort0156") {
53 P0156Idx = I;
54 }
55 }
56 EXPECT_NE(P0Idx, 0);
57 EXPECT_NE(P1Idx, 0);
58 EXPECT_NE(P5Idx, 0);
59 EXPECT_NE(P6Idx, 0);
60 EXPECT_NE(P05Idx, 0);
61 EXPECT_NE(P0156Idx, 0);
62 }
63
64 static void SetUpTestCase() {
65 LLVMInitializeX86TargetInfo();
66 LLVMInitializeX86Target();
67 LLVMInitializeX86TargetMC();
68 }
69
70 protected:
71 std::unique_ptr STI;
72 uint16_t P0Idx = 0;
73 uint16_t P1Idx = 0;
74 uint16_t P5Idx = 0;
75 uint16_t P6Idx = 0;
76 uint16_t P05Idx = 0;
77 uint16_t P0156Idx = 0;
78 };
79
80 TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P0) {
81 const auto Pressure =
82 computeIdealizedProcResPressure(STI->getSchedModel(), {{P0Idx, 2}});
83 EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(P0Idx, 2.0)));
84 }
85
86 TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P05) {
87 const auto Pressure =
88 computeIdealizedProcResPressure(STI->getSchedModel(), {{P05Idx, 2}});
89 EXPECT_THAT(Pressure,
90 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P5Idx, 1.0)));
91 }
92
93 TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_2P05_2P0156) {
94 const auto Pressure = computeIdealizedProcResPressure(
95 STI->getSchedModel(), {{P05Idx, 2}, {P0156Idx, 2}});
96 EXPECT_THAT(Pressure,
97 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
98 Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
99 }
100
101 TEST_F(AnalysisTest, ComputeIdealizedProcResPressure_1P1_1P05_2P0156) {
102 const auto Pressure = computeIdealizedProcResPressure(
103 STI->getSchedModel(), {{P1Idx, 1}, {P05Idx, 1}, {P0156Idx, 2}});
104 EXPECT_THAT(Pressure,
105 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
106 Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
107 }
108
109 } // namespace
110 } // namespace exegesis
111 } // namespace llvm
1414
1515 add_llvm_unittest(LLVMExegesisX86Tests
1616 AssemblerTest.cpp
17 AnalysisTest.cpp
1817 BenchmarkResultTest.cpp
18 RegisterAliasingTest.cpp
19 SchedClassResolutionTest.cpp
1920 SnippetGeneratorTest.cpp
20 RegisterAliasingTest.cpp
2121 TargetTest.cpp
2222 )
2323 target_link_libraries(LLVMExegesisX86Tests PRIVATE
0 //===-- SchedClassResolutionTest.cpp ----------------------------*- C++ -*-===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7
8 #include "SchedClassResolution.h"
9
10 #include
11 #include
12
13 #include "llvm/Support/TargetRegistry.h"
14 #include "llvm/Support/TargetSelect.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace llvm {
19 namespace exegesis {
20 namespace {
21
22 using testing::Pair;
23 using testing::UnorderedElementsAre;
24
25 class SchedClassResolutionTest : public ::testing::Test {
26 protected:
27 SchedClassResolutionTest() {
28 const std::string TT = "x86_64-unknown-linux";
29 std::string error;
30 const llvm::Target *const TheTarget =
31 llvm::TargetRegistry::lookupTarget(TT, error);
32 if (!TheTarget) {
33 llvm::errs() << error << "\n";
34 return;
35 }
36 STI.reset(TheTarget->createMCSubtargetInfo(TT, "haswell", ""));
37
38 // Compute the ProxResIdx of ports uses in tests.
39 const auto &SM = STI->getSchedModel();
40 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
41 const std::string Name = SM.getProcResource(I)->Name;
42 if (Name == "HWPort0") {
43 P0Idx = I;
44 } else if (Name == "HWPort1") {
45 P1Idx = I;
46 } else if (Name == "HWPort5") {
47 P5Idx = I;
48 } else if (Name == "HWPort6") {
49 P6Idx = I;
50 } else if (Name == "HWPort05") {
51 P05Idx = I;
52 } else if (Name == "HWPort0156") {
53 P0156Idx = I;
54 }
55 }
56 EXPECT_NE(P0Idx, 0);
57 EXPECT_NE(P1Idx, 0);
58 EXPECT_NE(P5Idx, 0);
59 EXPECT_NE(P6Idx, 0);
60 EXPECT_NE(P05Idx, 0);
61 EXPECT_NE(P0156Idx, 0);
62 }
63
64 static void SetUpTestCase() {
65 LLVMInitializeX86TargetInfo();
66 LLVMInitializeX86Target();
67 LLVMInitializeX86TargetMC();
68 }
69
70 protected:
71 std::unique_ptr STI;
72 uint16_t P0Idx = 0;
73 uint16_t P1Idx = 0;
74 uint16_t P5Idx = 0;
75 uint16_t P6Idx = 0;
76 uint16_t P05Idx = 0;
77 uint16_t P0156Idx = 0;
78 };
79
80 TEST_F(SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P0) {
81 const auto Pressure =
82 computeIdealizedProcResPressure(STI->getSchedModel(), {{P0Idx, 2}});
83 EXPECT_THAT(Pressure, UnorderedElementsAre(Pair(P0Idx, 2.0)));
84 }
85
86 TEST_F(SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P05) {
87 const auto Pressure =
88 computeIdealizedProcResPressure(STI->getSchedModel(), {{P05Idx, 2}});
89 EXPECT_THAT(Pressure,
90 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P5Idx, 1.0)));
91 }
92
93 TEST_F(SchedClassResolutionTest, ComputeIdealizedProcResPressure_2P05_2P0156) {
94 const auto Pressure = computeIdealizedProcResPressure(
95 STI->getSchedModel(), {{P05Idx, 2}, {P0156Idx, 2}});
96 EXPECT_THAT(Pressure,
97 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
98 Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
99 }
100
101 TEST_F(SchedClassResolutionTest,
102 ComputeIdealizedProcResPressure_1P1_1P05_2P0156) {
103 const auto Pressure = computeIdealizedProcResPressure(
104 STI->getSchedModel(), {{P1Idx, 1}, {P05Idx, 1}, {P0156Idx, 2}});
105 EXPECT_THAT(Pressure,
106 UnorderedElementsAre(Pair(P0Idx, 1.0), Pair(P1Idx, 1.0),
107 Pair(P5Idx, 1.0), Pair(P6Idx, 1.0)));
108 }
109
110 } // namespace
111 } // namespace exegesis
112 } // namespace llvm