1 //===- MCSchedule.cpp - Scheduling ------------------------------*- 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 // 9 // This file defines the default scheduling model. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/MC/MCSchedule.h" 14 #include "llvm/MC/MCInst.h" 15 #include "llvm/MC/MCInstrDesc.h" 16 #include "llvm/MC/MCInstrInfo.h" 17 #include "llvm/MC/MCSubtargetInfo.h" 18 #include <optional> 19 #include <type_traits> 20 21 using namespace llvm; 22 23 static_assert(std::is_pod<MCSchedModel>::value, 24 "We shouldn't have a static constructor here"); 25 const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth, 26 DefaultMicroOpBufferSize, 27 DefaultLoopMicroOpBufferSize, 28 DefaultLoadLatency, 29 DefaultHighLatency, 30 DefaultMispredictPenalty, 31 false, 32 true, 33 0, 34 nullptr, 35 nullptr, 36 0, 37 0, 38 nullptr, 39 nullptr}; 40 41 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI, 42 const MCSchedClassDesc &SCDesc) { 43 int Latency = 0; 44 for (unsigned DefIdx = 0, DefEnd = SCDesc.NumWriteLatencyEntries; 45 DefIdx != DefEnd; ++DefIdx) { 46 // Lookup the definition's write latency in SubtargetInfo. 47 const MCWriteLatencyEntry *WLEntry = 48 STI.getWriteLatencyEntry(&SCDesc, DefIdx); 49 // Early exit if we found an invalid latency. 50 if (WLEntry->Cycles < 0) 51 return WLEntry->Cycles; 52 Latency = std::max(Latency, static_cast<int>(WLEntry->Cycles)); 53 } 54 return Latency; 55 } 56 57 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI, 58 unsigned SchedClass) const { 59 const MCSchedClassDesc &SCDesc = *getSchedClassDesc(SchedClass); 60 if (!SCDesc.isValid()) 61 return 0; 62 if (!SCDesc.isVariant()) 63 return MCSchedModel::computeInstrLatency(STI, SCDesc); 64 65 llvm_unreachable("unsupported variant scheduling class"); 66 } 67 68 int MCSchedModel::computeInstrLatency(const MCSubtargetInfo &STI, 69 const MCInstrInfo &MCII, 70 const MCInst &Inst) const { 71 unsigned SchedClass = MCII.get(Inst.getOpcode()).getSchedClass(); 72 const MCSchedClassDesc *SCDesc = getSchedClassDesc(SchedClass); 73 if (!SCDesc->isValid()) 74 return 0; 75 76 unsigned CPUID = getProcessorID(); 77 while (SCDesc->isVariant()) { 78 SchedClass = STI.resolveVariantSchedClass(SchedClass, &Inst, &MCII, CPUID); 79 SCDesc = getSchedClassDesc(SchedClass); 80 } 81 82 if (SchedClass) 83 return MCSchedModel::computeInstrLatency(STI, *SCDesc); 84 85 llvm_unreachable("unsupported variant scheduling class"); 86 } 87 88 double 89 MCSchedModel::getReciprocalThroughput(const MCSubtargetInfo &STI, 90 const MCSchedClassDesc &SCDesc) { 91 std::optional<double> Throughput; 92 const MCSchedModel &SM = STI.getSchedModel(); 93 const MCWriteProcResEntry *I = STI.getWriteProcResBegin(&SCDesc); 94 const MCWriteProcResEntry *E = STI.getWriteProcResEnd(&SCDesc); 95 for (; I != E; ++I) { 96 if (!I->Cycles) 97 continue; 98 unsigned NumUnits = SM.getProcResource(I->ProcResourceIdx)->NumUnits; 99 double Temp = NumUnits * 1.0 / I->Cycles; 100 Throughput = Throughput ? std::min(*Throughput, Temp) : Temp; 101 } 102 if (Throughput) 103 return 1.0 / *Throughput; 104 105 // If no throughput value was calculated, assume that we can execute at the 106 // maximum issue width scaled by number of micro-ops for the schedule class. 107 return ((double)SCDesc.NumMicroOps) / SM.IssueWidth; 108 } 109 110 double 111 MCSchedModel::getReciprocalThroughput(const MCSubtargetInfo &STI, 112 const MCInstrInfo &MCII, 113 const MCInst &Inst) const { 114 unsigned SchedClass = MCII.get(Inst.getOpcode()).getSchedClass(); 115 const MCSchedClassDesc *SCDesc = getSchedClassDesc(SchedClass); 116 117 // If there's no valid class, assume that the instruction executes/completes 118 // at the maximum issue width. 119 if (!SCDesc->isValid()) 120 return 1.0 / IssueWidth; 121 122 unsigned CPUID = getProcessorID(); 123 while (SCDesc->isVariant()) { 124 SchedClass = STI.resolveVariantSchedClass(SchedClass, &Inst, &MCII, CPUID); 125 SCDesc = getSchedClassDesc(SchedClass); 126 } 127 128 if (SchedClass) 129 return MCSchedModel::getReciprocalThroughput(STI, *SCDesc); 130 131 llvm_unreachable("unsupported variant scheduling class"); 132 } 133 134 double 135 MCSchedModel::getReciprocalThroughput(unsigned SchedClass, 136 const InstrItineraryData &IID) { 137 std::optional<double> Throughput; 138 const InstrStage *I = IID.beginStage(SchedClass); 139 const InstrStage *E = IID.endStage(SchedClass); 140 for (; I != E; ++I) { 141 if (!I->getCycles()) 142 continue; 143 double Temp = llvm::popcount(I->getUnits()) * 1.0 / I->getCycles(); 144 Throughput = Throughput ? std::min(*Throughput, Temp) : Temp; 145 } 146 if (Throughput) 147 return 1.0 / *Throughput; 148 149 // If there are no execution resources specified for this class, then assume 150 // that it can execute at the maximum default issue width. 151 return 1.0 / DefaultIssueWidth; 152 } 153 154 unsigned 155 MCSchedModel::getForwardingDelayCycles(ArrayRef<MCReadAdvanceEntry> Entries, 156 unsigned WriteResourceID) { 157 if (Entries.empty()) 158 return 0; 159 160 int DelayCycles = 0; 161 for (const MCReadAdvanceEntry &E : Entries) { 162 if (E.WriteResourceID != WriteResourceID) 163 continue; 164 DelayCycles = std::min(DelayCycles, E.Cycles); 165 } 166 167 return std::abs(DelayCycles); 168 } 169