xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-exegesis/lib/SchedClassResolution.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===-- SchedClassResolution.cpp --------------------------------*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick 
909467b48Spatrick #include "SchedClassResolution.h"
1009467b48Spatrick #include "BenchmarkResult.h"
1109467b48Spatrick #include "llvm/ADT/STLExtras.h"
1209467b48Spatrick #include "llvm/MC/MCAsmInfo.h"
13*d415bd75Srobert #include "llvm/MCA/Support.h"
1409467b48Spatrick #include "llvm/Support/FormatVariadic.h"
1509467b48Spatrick #include <limits>
1609467b48Spatrick #include <unordered_set>
1709467b48Spatrick #include <vector>
1809467b48Spatrick 
1909467b48Spatrick namespace llvm {
2009467b48Spatrick namespace exegesis {
2109467b48Spatrick 
2209467b48Spatrick // Return the non-redundant list of WriteProcRes used by the given sched class.
2309467b48Spatrick // The scheduling model for LLVM is such that each instruction has a certain
2409467b48Spatrick // number of uops which consume resources which are described by WriteProcRes
2509467b48Spatrick // entries. Each entry describe how many cycles are spent on a specific ProcRes
2609467b48Spatrick // kind.
2709467b48Spatrick // For example, an instruction might have 3 uOps, one dispatching on P0
2809467b48Spatrick // (ProcResIdx=1) and two on P06 (ProcResIdx = 7).
2909467b48Spatrick // Note that LLVM additionally denormalizes resource consumption to include
3009467b48Spatrick // usage of super resources by subresources. So in practice if there exists a
3109467b48Spatrick // P016 (ProcResIdx=10), then the cycles consumed by P0 are also consumed by
3209467b48Spatrick // P06 (ProcResIdx = 7) and P016 (ProcResIdx = 10), and the resources consumed
3309467b48Spatrick // by P06 are also consumed by P016. In the figure below, parenthesized cycles
3409467b48Spatrick // denote implied usage of superresources by subresources:
3509467b48Spatrick //            P0      P06    P016
3609467b48Spatrick //     uOp1    1      (1)     (1)
3709467b48Spatrick //     uOp2            1      (1)
3809467b48Spatrick //     uOp3            1      (1)
3909467b48Spatrick //     =============================
4009467b48Spatrick //             1       3       3
4109467b48Spatrick // Eventually we end up with three entries for the WriteProcRes of the
4209467b48Spatrick // instruction:
4309467b48Spatrick //    {ProcResIdx=1,  Cycles=1}  // P0
4409467b48Spatrick //    {ProcResIdx=7,  Cycles=3}  // P06
4509467b48Spatrick //    {ProcResIdx=10, Cycles=3}  // P016
4609467b48Spatrick //
4709467b48Spatrick // Note that in this case, P016 does not contribute any cycles, so it would
4809467b48Spatrick // be removed by this function.
49*d415bd75Srobert // FIXME: Merge this with the equivalent in llvm-mca.
5009467b48Spatrick static SmallVector<MCWriteProcResEntry, 8>
getNonRedundantWriteProcRes(const MCSchedClassDesc & SCDesc,const MCSubtargetInfo & STI)5109467b48Spatrick getNonRedundantWriteProcRes(const MCSchedClassDesc &SCDesc,
5209467b48Spatrick                             const MCSubtargetInfo &STI) {
5309467b48Spatrick   SmallVector<MCWriteProcResEntry, 8> Result;
5409467b48Spatrick   const auto &SM = STI.getSchedModel();
5509467b48Spatrick   const unsigned NumProcRes = SM.getNumProcResourceKinds();
5609467b48Spatrick 
57*d415bd75Srobert   // Collect resource masks.
58*d415bd75Srobert   SmallVector<uint64_t> ProcResourceMasks(NumProcRes);
59*d415bd75Srobert   mca::computeProcResourceMasks(SM, ProcResourceMasks);
60*d415bd75Srobert 
61*d415bd75Srobert   // Sort entries by smaller resources for (basic) topological ordering.
62*d415bd75Srobert   using ResourceMaskAndEntry = std::pair<uint64_t, const MCWriteProcResEntry *>;
63*d415bd75Srobert   SmallVector<ResourceMaskAndEntry, 8> ResourceMaskAndEntries;
6409467b48Spatrick   for (const auto *WPR = STI.getWriteProcResBegin(&SCDesc),
6509467b48Spatrick                   *const WPREnd = STI.getWriteProcResEnd(&SCDesc);
6609467b48Spatrick        WPR != WPREnd; ++WPR) {
67*d415bd75Srobert     uint64_t Mask = ProcResourceMasks[WPR->ProcResourceIdx];
68*d415bd75Srobert     ResourceMaskAndEntries.push_back({Mask, WPR});
69*d415bd75Srobert   }
70*d415bd75Srobert   sort(ResourceMaskAndEntries,
71*d415bd75Srobert        [](const ResourceMaskAndEntry &A, const ResourceMaskAndEntry &B) {
72*d415bd75Srobert          unsigned popcntA = llvm::popcount(A.first);
73*d415bd75Srobert          unsigned popcntB = llvm::popcount(B.first);
74*d415bd75Srobert          if (popcntA < popcntB)
75*d415bd75Srobert            return true;
76*d415bd75Srobert          if (popcntA > popcntB)
77*d415bd75Srobert            return false;
78*d415bd75Srobert          return A.first < B.first;
79*d415bd75Srobert        });
80*d415bd75Srobert 
81*d415bd75Srobert   SmallVector<float, 32> ProcResUnitUsage(NumProcRes);
82*d415bd75Srobert   for (const ResourceMaskAndEntry &Entry : ResourceMaskAndEntries) {
83*d415bd75Srobert     const MCWriteProcResEntry *WPR = Entry.second;
8409467b48Spatrick     const MCProcResourceDesc *const ProcResDesc =
8509467b48Spatrick         SM.getProcResource(WPR->ProcResourceIdx);
8609467b48Spatrick     if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
8709467b48Spatrick       // This is a ProcResUnit.
8809467b48Spatrick       Result.push_back({WPR->ProcResourceIdx, WPR->Cycles});
8909467b48Spatrick       ProcResUnitUsage[WPR->ProcResourceIdx] += WPR->Cycles;
9009467b48Spatrick     } else {
9109467b48Spatrick       // This is a ProcResGroup. First see if it contributes any cycles or if
9209467b48Spatrick       // it has cycles just from subunits.
9309467b48Spatrick       float RemainingCycles = WPR->Cycles;
9409467b48Spatrick       for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
9509467b48Spatrick            SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
9609467b48Spatrick            ++SubResIdx) {
9709467b48Spatrick         RemainingCycles -= ProcResUnitUsage[*SubResIdx];
9809467b48Spatrick       }
9909467b48Spatrick       if (RemainingCycles < 0.01f) {
10009467b48Spatrick         // The ProcResGroup contributes no cycles of its own.
10109467b48Spatrick         continue;
10209467b48Spatrick       }
10309467b48Spatrick       // The ProcResGroup contributes `RemainingCycles` cycles of its own.
10409467b48Spatrick       Result.push_back({WPR->ProcResourceIdx,
10509467b48Spatrick                         static_cast<uint16_t>(std::round(RemainingCycles))});
10609467b48Spatrick       // Spread the remaining cycles over all subunits.
10709467b48Spatrick       for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
10809467b48Spatrick            SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
10909467b48Spatrick            ++SubResIdx) {
11009467b48Spatrick         ProcResUnitUsage[*SubResIdx] += RemainingCycles / ProcResDesc->NumUnits;
11109467b48Spatrick       }
11209467b48Spatrick     }
11309467b48Spatrick   }
11409467b48Spatrick   return Result;
11509467b48Spatrick }
11609467b48Spatrick 
11709467b48Spatrick // Distributes a pressure budget as evenly as possible on the provided subunits
11809467b48Spatrick // given the already existing port pressure distribution.
11909467b48Spatrick //
12009467b48Spatrick // The algorithm is as follows: while there is remaining pressure to
12109467b48Spatrick // distribute, find the subunits with minimal pressure, and distribute
12209467b48Spatrick // remaining pressure equally up to the pressure of the unit with
12309467b48Spatrick // second-to-minimal pressure.
12409467b48Spatrick // For example, let's assume we want to distribute 2*P1256
12509467b48Spatrick // (Subunits = [P1,P2,P5,P6]), and the starting DensePressure is:
12609467b48Spatrick //     DensePressure =        P0   P1   P2   P3   P4   P5   P6   P7
12709467b48Spatrick //                           0.1  0.3  0.2  0.0  0.0  0.5  0.5  0.5
12809467b48Spatrick //     RemainingPressure = 2.0
12909467b48Spatrick // We sort the subunits by pressure:
13009467b48Spatrick //     Subunits = [(P2,p=0.2), (P1,p=0.3), (P5,p=0.5), (P6, p=0.5)]
13109467b48Spatrick // We'll first start by the subunits with minimal pressure, which are at
13209467b48Spatrick // the beginning of the sorted array. In this example there is one (P2).
13309467b48Spatrick // The subunit with second-to-minimal pressure is the next one in the
13409467b48Spatrick // array (P1). So we distribute 0.1 pressure to P2, and remove 0.1 cycles
13509467b48Spatrick // from the budget.
13609467b48Spatrick //     Subunits = [(P2,p=0.3), (P1,p=0.3), (P5,p=0.5), (P5,p=0.5)]
13709467b48Spatrick //     RemainingPressure = 1.9
13809467b48Spatrick // We repeat this process: distribute 0.2 pressure on each of the minimal
13909467b48Spatrick // P2 and P1, decrease budget by 2*0.2:
14009467b48Spatrick //     Subunits = [(P2,p=0.5), (P1,p=0.5), (P5,p=0.5), (P5,p=0.5)]
14109467b48Spatrick //     RemainingPressure = 1.5
14209467b48Spatrick // There are no second-to-minimal subunits so we just share the remaining
14309467b48Spatrick // budget (1.5 cycles) equally:
14409467b48Spatrick //     Subunits = [(P2,p=0.875), (P1,p=0.875), (P5,p=0.875), (P5,p=0.875)]
14509467b48Spatrick //     RemainingPressure = 0.0
14609467b48Spatrick // We stop as there is no remaining budget to distribute.
distributePressure(float RemainingPressure,SmallVector<uint16_t,32> Subunits,SmallVector<float,32> & DensePressure)14709467b48Spatrick static void distributePressure(float RemainingPressure,
14809467b48Spatrick                                SmallVector<uint16_t, 32> Subunits,
14909467b48Spatrick                                SmallVector<float, 32> &DensePressure) {
15009467b48Spatrick   // Find the number of subunits with minimal pressure (they are at the
15109467b48Spatrick   // front).
15209467b48Spatrick   sort(Subunits, [&DensePressure](const uint16_t A, const uint16_t B) {
15309467b48Spatrick     return DensePressure[A] < DensePressure[B];
15409467b48Spatrick   });
15509467b48Spatrick   const auto getPressureForSubunit = [&DensePressure,
15609467b48Spatrick                                       &Subunits](size_t I) -> float & {
15709467b48Spatrick     return DensePressure[Subunits[I]];
15809467b48Spatrick   };
15909467b48Spatrick   size_t NumMinimalSU = 1;
16009467b48Spatrick   while (NumMinimalSU < Subunits.size() &&
16109467b48Spatrick          getPressureForSubunit(NumMinimalSU) == getPressureForSubunit(0)) {
16209467b48Spatrick     ++NumMinimalSU;
16309467b48Spatrick   }
16409467b48Spatrick   while (RemainingPressure > 0.0f) {
16509467b48Spatrick     if (NumMinimalSU == Subunits.size()) {
16609467b48Spatrick       // All units are minimal, just distribute evenly and be done.
16709467b48Spatrick       for (size_t I = 0; I < NumMinimalSU; ++I) {
16809467b48Spatrick         getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
16909467b48Spatrick       }
17009467b48Spatrick       return;
17109467b48Spatrick     }
17209467b48Spatrick     // Distribute the remaining pressure equally.
17309467b48Spatrick     const float MinimalPressure = getPressureForSubunit(NumMinimalSU - 1);
17409467b48Spatrick     const float SecondToMinimalPressure = getPressureForSubunit(NumMinimalSU);
17509467b48Spatrick     assert(MinimalPressure < SecondToMinimalPressure);
17609467b48Spatrick     const float Increment = SecondToMinimalPressure - MinimalPressure;
17709467b48Spatrick     if (RemainingPressure <= NumMinimalSU * Increment) {
17809467b48Spatrick       // There is not enough remaining pressure.
17909467b48Spatrick       for (size_t I = 0; I < NumMinimalSU; ++I) {
18009467b48Spatrick         getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
18109467b48Spatrick       }
18209467b48Spatrick       return;
18309467b48Spatrick     }
18409467b48Spatrick     // Bump all minimal pressure subunits to `SecondToMinimalPressure`.
18509467b48Spatrick     for (size_t I = 0; I < NumMinimalSU; ++I) {
18609467b48Spatrick       getPressureForSubunit(I) = SecondToMinimalPressure;
18709467b48Spatrick       RemainingPressure -= SecondToMinimalPressure;
18809467b48Spatrick     }
18909467b48Spatrick     while (NumMinimalSU < Subunits.size() &&
19009467b48Spatrick            getPressureForSubunit(NumMinimalSU) == SecondToMinimalPressure) {
19109467b48Spatrick       ++NumMinimalSU;
19209467b48Spatrick     }
19309467b48Spatrick   }
19409467b48Spatrick }
19509467b48Spatrick 
19609467b48Spatrick std::vector<std::pair<uint16_t, float>>
computeIdealizedProcResPressure(const MCSchedModel & SM,SmallVector<MCWriteProcResEntry,8> WPRS)19709467b48Spatrick computeIdealizedProcResPressure(const MCSchedModel &SM,
19809467b48Spatrick                                 SmallVector<MCWriteProcResEntry, 8> WPRS) {
19909467b48Spatrick   // DensePressure[I] is the port pressure for Proc Resource I.
20009467b48Spatrick   SmallVector<float, 32> DensePressure(SM.getNumProcResourceKinds());
20109467b48Spatrick   sort(WPRS, [](const MCWriteProcResEntry &A, const MCWriteProcResEntry &B) {
20209467b48Spatrick     return A.ProcResourceIdx < B.ProcResourceIdx;
20309467b48Spatrick   });
20409467b48Spatrick   for (const MCWriteProcResEntry &WPR : WPRS) {
20509467b48Spatrick     // Get units for the entry.
20609467b48Spatrick     const MCProcResourceDesc *const ProcResDesc =
20709467b48Spatrick         SM.getProcResource(WPR.ProcResourceIdx);
20809467b48Spatrick     if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
20909467b48Spatrick       // This is a ProcResUnit.
21009467b48Spatrick       DensePressure[WPR.ProcResourceIdx] += WPR.Cycles;
21109467b48Spatrick     } else {
21209467b48Spatrick       // This is a ProcResGroup.
21309467b48Spatrick       SmallVector<uint16_t, 32> Subunits(ProcResDesc->SubUnitsIdxBegin,
21409467b48Spatrick                                          ProcResDesc->SubUnitsIdxBegin +
21509467b48Spatrick                                              ProcResDesc->NumUnits);
21609467b48Spatrick       distributePressure(WPR.Cycles, Subunits, DensePressure);
21709467b48Spatrick     }
21809467b48Spatrick   }
21909467b48Spatrick   // Turn dense pressure into sparse pressure by removing zero entries.
22009467b48Spatrick   std::vector<std::pair<uint16_t, float>> Pressure;
22109467b48Spatrick   for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
22209467b48Spatrick     if (DensePressure[I] > 0.0f)
22309467b48Spatrick       Pressure.emplace_back(I, DensePressure[I]);
22409467b48Spatrick   }
22509467b48Spatrick   return Pressure;
22609467b48Spatrick }
22709467b48Spatrick 
ResolvedSchedClass(const MCSubtargetInfo & STI,unsigned ResolvedSchedClassId,bool WasVariant)22809467b48Spatrick ResolvedSchedClass::ResolvedSchedClass(const MCSubtargetInfo &STI,
22909467b48Spatrick                                        unsigned ResolvedSchedClassId,
23009467b48Spatrick                                        bool WasVariant)
23109467b48Spatrick     : SchedClassId(ResolvedSchedClassId),
23209467b48Spatrick       SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)),
23309467b48Spatrick       WasVariant(WasVariant),
23409467b48Spatrick       NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)),
23509467b48Spatrick       IdealizedProcResPressure(computeIdealizedProcResPressure(
23609467b48Spatrick           STI.getSchedModel(), NonRedundantWriteProcRes)) {
23709467b48Spatrick   assert((SCDesc == nullptr || !SCDesc->isVariant()) &&
23809467b48Spatrick          "ResolvedSchedClass should never be variant");
23909467b48Spatrick }
24009467b48Spatrick 
ResolveVariantSchedClassId(const MCSubtargetInfo & STI,const MCInstrInfo & InstrInfo,unsigned SchedClassId,const MCInst & MCI)24109467b48Spatrick static unsigned ResolveVariantSchedClassId(const MCSubtargetInfo &STI,
24273471bf0Spatrick                                            const MCInstrInfo &InstrInfo,
24309467b48Spatrick                                            unsigned SchedClassId,
24409467b48Spatrick                                            const MCInst &MCI) {
24509467b48Spatrick   const auto &SM = STI.getSchedModel();
24673471bf0Spatrick   while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant()) {
24773471bf0Spatrick     SchedClassId = STI.resolveVariantSchedClass(SchedClassId, &MCI, &InstrInfo,
24873471bf0Spatrick                                                 SM.getProcessorID());
24973471bf0Spatrick   }
25009467b48Spatrick   return SchedClassId;
25109467b48Spatrick }
25209467b48Spatrick 
25309467b48Spatrick std::pair<unsigned /*SchedClassId*/, bool /*WasVariant*/>
resolveSchedClassId(const MCSubtargetInfo & SubtargetInfo,const MCInstrInfo & InstrInfo,const MCInst & MCI)25409467b48Spatrick ResolvedSchedClass::resolveSchedClassId(const MCSubtargetInfo &SubtargetInfo,
25509467b48Spatrick                                         const MCInstrInfo &InstrInfo,
25609467b48Spatrick                                         const MCInst &MCI) {
25709467b48Spatrick   unsigned SchedClassId = InstrInfo.get(MCI.getOpcode()).getSchedClass();
25809467b48Spatrick   const bool WasVariant = SchedClassId && SubtargetInfo.getSchedModel()
25909467b48Spatrick                                               .getSchedClassDesc(SchedClassId)
26009467b48Spatrick                                               ->isVariant();
26173471bf0Spatrick   SchedClassId =
26273471bf0Spatrick       ResolveVariantSchedClassId(SubtargetInfo, InstrInfo, SchedClassId, MCI);
26309467b48Spatrick   return std::make_pair(SchedClassId, WasVariant);
26409467b48Spatrick }
26509467b48Spatrick 
26609467b48Spatrick // Returns a ProxResIdx by id or name.
findProcResIdx(const MCSubtargetInfo & STI,const StringRef NameOrId)26709467b48Spatrick static unsigned findProcResIdx(const MCSubtargetInfo &STI,
26809467b48Spatrick                                const StringRef NameOrId) {
26909467b48Spatrick   // Interpret the key as an ProcResIdx.
27009467b48Spatrick   unsigned ProcResIdx = 0;
27109467b48Spatrick   if (to_integer(NameOrId, ProcResIdx, 10))
27209467b48Spatrick     return ProcResIdx;
27309467b48Spatrick   // Interpret the key as a ProcRes name.
27409467b48Spatrick   const auto &SchedModel = STI.getSchedModel();
27509467b48Spatrick   for (int I = 0, E = SchedModel.getNumProcResourceKinds(); I < E; ++I) {
27609467b48Spatrick     if (NameOrId == SchedModel.getProcResource(I)->Name)
27709467b48Spatrick       return I;
27809467b48Spatrick   }
27909467b48Spatrick   return 0;
28009467b48Spatrick }
28109467b48Spatrick 
getAsPoint(InstructionBenchmark::ModeE Mode,const MCSubtargetInfo & STI,ArrayRef<PerInstructionStats> Representative) const28209467b48Spatrick std::vector<BenchmarkMeasure> ResolvedSchedClass::getAsPoint(
28309467b48Spatrick     InstructionBenchmark::ModeE Mode, const MCSubtargetInfo &STI,
28409467b48Spatrick     ArrayRef<PerInstructionStats> Representative) const {
28509467b48Spatrick   const size_t NumMeasurements = Representative.size();
28609467b48Spatrick 
28709467b48Spatrick   std::vector<BenchmarkMeasure> SchedClassPoint(NumMeasurements);
28809467b48Spatrick 
28909467b48Spatrick   if (Mode == InstructionBenchmark::Latency) {
29009467b48Spatrick     assert(NumMeasurements == 1 && "Latency is a single measure.");
29109467b48Spatrick     BenchmarkMeasure &LatencyMeasure = SchedClassPoint[0];
29209467b48Spatrick 
29309467b48Spatrick     // Find the latency.
29409467b48Spatrick     LatencyMeasure.PerInstructionValue = 0.0;
29509467b48Spatrick 
29609467b48Spatrick     for (unsigned I = 0; I < SCDesc->NumWriteLatencyEntries; ++I) {
29709467b48Spatrick       const MCWriteLatencyEntry *const WLE =
29809467b48Spatrick           STI.getWriteLatencyEntry(SCDesc, I);
29909467b48Spatrick       LatencyMeasure.PerInstructionValue =
30009467b48Spatrick           std::max<double>(LatencyMeasure.PerInstructionValue, WLE->Cycles);
30109467b48Spatrick     }
30209467b48Spatrick   } else if (Mode == InstructionBenchmark::Uops) {
30309467b48Spatrick     for (auto I : zip(SchedClassPoint, Representative)) {
30409467b48Spatrick       BenchmarkMeasure &Measure = std::get<0>(I);
30509467b48Spatrick       const PerInstructionStats &Stats = std::get<1>(I);
30609467b48Spatrick 
30709467b48Spatrick       StringRef Key = Stats.key();
30809467b48Spatrick       uint16_t ProcResIdx = findProcResIdx(STI, Key);
30909467b48Spatrick       if (ProcResIdx > 0) {
31009467b48Spatrick         // Find the pressure on ProcResIdx `Key`.
31173471bf0Spatrick         const auto ProcResPressureIt =
31273471bf0Spatrick             llvm::find_if(IdealizedProcResPressure,
31309467b48Spatrick                           [ProcResIdx](const std::pair<uint16_t, float> &WPR) {
31409467b48Spatrick                             return WPR.first == ProcResIdx;
31509467b48Spatrick                           });
31609467b48Spatrick         Measure.PerInstructionValue =
31709467b48Spatrick             ProcResPressureIt == IdealizedProcResPressure.end()
31809467b48Spatrick                 ? 0.0
31909467b48Spatrick                 : ProcResPressureIt->second;
32009467b48Spatrick       } else if (Key == "NumMicroOps") {
32109467b48Spatrick         Measure.PerInstructionValue = SCDesc->NumMicroOps;
32209467b48Spatrick       } else {
32309467b48Spatrick         errs() << "expected `key` to be either a ProcResIdx or a ProcRes "
32409467b48Spatrick                   "name, got "
32509467b48Spatrick                << Key << "\n";
32609467b48Spatrick         return {};
32709467b48Spatrick       }
32809467b48Spatrick     }
32909467b48Spatrick   } else if (Mode == InstructionBenchmark::InverseThroughput) {
33009467b48Spatrick     assert(NumMeasurements == 1 && "Inverse Throughput is a single measure.");
33109467b48Spatrick     BenchmarkMeasure &RThroughputMeasure = SchedClassPoint[0];
33209467b48Spatrick 
33309467b48Spatrick     RThroughputMeasure.PerInstructionValue =
33409467b48Spatrick         MCSchedModel::getReciprocalThroughput(STI, *SCDesc);
33509467b48Spatrick   } else {
33609467b48Spatrick     llvm_unreachable("unimplemented measurement matching mode");
33709467b48Spatrick   }
33809467b48Spatrick 
33909467b48Spatrick   return SchedClassPoint;
34009467b48Spatrick }
34109467b48Spatrick 
34209467b48Spatrick } // namespace exegesis
34309467b48Spatrick } // namespace llvm
344