1 //===--------------------- Instruction.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 // 9 // This file defines abstractions used by the Pipeline to model register reads, 10 // register writes and instructions. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/MCA/Instruction.h" 15 #include "llvm/Support/Debug.h" 16 #include "llvm/Support/raw_ostream.h" 17 18 namespace llvm { 19 namespace mca { 20 21 void WriteState::writeStartEvent(unsigned IID, unsigned RegID, 22 unsigned Cycles) { 23 CRD.IID = IID; 24 CRD.RegID = RegID; 25 CRD.Cycles = Cycles; 26 DependentWriteCyclesLeft = Cycles; 27 DependentWrite = nullptr; 28 } 29 30 void ReadState::writeStartEvent(unsigned IID, unsigned RegID, unsigned Cycles) { 31 assert(DependentWrites); 32 assert(CyclesLeft == UNKNOWN_CYCLES); 33 34 // This read may be dependent on more than one write. This typically occurs 35 // when a definition is the result of multiple writes where at least one 36 // write does a partial register update. 37 // The HW is forced to do some extra bookkeeping to track of all the 38 // dependent writes, and implement a merging scheme for the partial writes. 39 --DependentWrites; 40 if (TotalCycles < Cycles) { 41 CRD.IID = IID; 42 CRD.RegID = RegID; 43 CRD.Cycles = Cycles; 44 TotalCycles = Cycles; 45 } 46 47 if (!DependentWrites) { 48 CyclesLeft = TotalCycles; 49 IsReady = !CyclesLeft; 50 } 51 } 52 53 void WriteState::onInstructionIssued(unsigned IID) { 54 assert(CyclesLeft == UNKNOWN_CYCLES); 55 // Update the number of cycles left based on the WriteDescriptor info. 56 CyclesLeft = getLatency(); 57 58 // Now that the time left before write-back is known, notify 59 // all the users. 60 for (const std::pair<ReadState *, int> &User : Users) { 61 ReadState *RS = User.first; 62 unsigned ReadCycles = std::max(0, CyclesLeft - User.second); 63 RS->writeStartEvent(IID, RegisterID, ReadCycles); 64 } 65 66 // Notify any writes that are in a false dependency with this write. 67 if (PartialWrite) 68 PartialWrite->writeStartEvent(IID, RegisterID, CyclesLeft); 69 } 70 71 void WriteState::addUser(unsigned IID, ReadState *User, int ReadAdvance) { 72 // If CyclesLeft is different than -1, then we don't need to 73 // update the list of users. We can just notify the user with 74 // the actual number of cycles left (which may be zero). 75 if (CyclesLeft != UNKNOWN_CYCLES) { 76 unsigned ReadCycles = std::max(0, CyclesLeft - ReadAdvance); 77 User->writeStartEvent(IID, RegisterID, ReadCycles); 78 return; 79 } 80 81 Users.emplace_back(User, ReadAdvance); 82 } 83 84 void WriteState::addUser(unsigned IID, WriteState *User) { 85 if (CyclesLeft != UNKNOWN_CYCLES) { 86 User->writeStartEvent(IID, RegisterID, std::max(0, CyclesLeft)); 87 return; 88 } 89 90 assert(!PartialWrite && "PartialWrite already set!"); 91 PartialWrite = User; 92 User->setDependentWrite(this); 93 } 94 95 void WriteState::cycleEvent() { 96 // Note: CyclesLeft can be a negative number. It is an error to 97 // make it an unsigned quantity because users of this write may 98 // specify a negative ReadAdvance. 99 if (CyclesLeft != UNKNOWN_CYCLES) 100 CyclesLeft--; 101 102 if (DependentWriteCyclesLeft) 103 DependentWriteCyclesLeft--; 104 } 105 106 void ReadState::cycleEvent() { 107 // Update the total number of cycles. 108 if (DependentWrites && TotalCycles) { 109 --TotalCycles; 110 return; 111 } 112 113 // Bail out immediately if we don't know how many cycles are left. 114 if (CyclesLeft == UNKNOWN_CYCLES) 115 return; 116 117 if (CyclesLeft) { 118 --CyclesLeft; 119 IsReady = !CyclesLeft; 120 } 121 } 122 123 #ifndef NDEBUG 124 void WriteState::dump() const { 125 dbgs() << "{ OpIdx=" << WD->OpIndex << ", Lat=" << getLatency() << ", RegID " 126 << getRegisterID() << ", Cycles Left=" << getCyclesLeft() << " }"; 127 } 128 129 void WriteRef::dump() const { 130 dbgs() << "IID=" << getSourceIndex() << ' '; 131 if (isValid()) 132 getWriteState()->dump(); 133 else 134 dbgs() << "(null)"; 135 } 136 #endif 137 138 const CriticalDependency &Instruction::computeCriticalRegDep() { 139 if (CriticalRegDep.Cycles) 140 return CriticalRegDep; 141 142 unsigned MaxLatency = 0; 143 for (const WriteState &WS : getDefs()) { 144 const CriticalDependency &WriteCRD = WS.getCriticalRegDep(); 145 if (WriteCRD.Cycles > MaxLatency) 146 CriticalRegDep = WriteCRD; 147 } 148 149 for (const ReadState &RS : getUses()) { 150 const CriticalDependency &ReadCRD = RS.getCriticalRegDep(); 151 if (ReadCRD.Cycles > MaxLatency) 152 CriticalRegDep = ReadCRD; 153 } 154 155 return CriticalRegDep; 156 } 157 158 void Instruction::dispatch(unsigned RCUToken) { 159 assert(Stage == IS_INVALID); 160 Stage = IS_DISPATCHED; 161 RCUTokenID = RCUToken; 162 163 // Check if input operands are already available. 164 if (updateDispatched()) 165 updatePending(); 166 } 167 168 void Instruction::execute(unsigned IID) { 169 assert(Stage == IS_READY); 170 Stage = IS_EXECUTING; 171 172 // Set the cycles left before the write-back stage. 173 CyclesLeft = getLatency(); 174 175 for (WriteState &WS : getDefs()) 176 WS.onInstructionIssued(IID); 177 178 // Transition to the "executed" stage if this is a zero-latency instruction. 179 if (!CyclesLeft) 180 Stage = IS_EXECUTED; 181 } 182 183 void Instruction::forceExecuted() { 184 assert(Stage == IS_READY && "Invalid internal state!"); 185 CyclesLeft = 0; 186 Stage = IS_EXECUTED; 187 } 188 189 bool Instruction::updatePending() { 190 assert(isPending() && "Unexpected instruction stage found!"); 191 192 if (!all_of(getUses(), [](const ReadState &Use) { return Use.isReady(); })) 193 return false; 194 195 // A partial register write cannot complete before a dependent write. 196 if (!all_of(getDefs(), [](const WriteState &Def) { return Def.isReady(); })) 197 return false; 198 199 Stage = IS_READY; 200 return true; 201 } 202 203 bool Instruction::updateDispatched() { 204 assert(isDispatched() && "Unexpected instruction stage found!"); 205 206 if (!all_of(getUses(), [](const ReadState &Use) { 207 return Use.isPending() || Use.isReady(); 208 })) 209 return false; 210 211 // A partial register write cannot complete before a dependent write. 212 if (!all_of(getDefs(), 213 [](const WriteState &Def) { return !Def.getDependentWrite(); })) 214 return false; 215 216 Stage = IS_PENDING; 217 return true; 218 } 219 220 void Instruction::update() { 221 if (isDispatched()) 222 updateDispatched(); 223 if (isPending()) 224 updatePending(); 225 } 226 227 void Instruction::cycleEvent() { 228 if (isReady()) 229 return; 230 231 if (isDispatched() || isPending()) { 232 for (ReadState &Use : getUses()) 233 Use.cycleEvent(); 234 235 for (WriteState &Def : getDefs()) 236 Def.cycleEvent(); 237 238 update(); 239 return; 240 } 241 242 assert(isExecuting() && "Instruction not in-flight?"); 243 assert(CyclesLeft && "Instruction already executed?"); 244 for (WriteState &Def : getDefs()) 245 Def.cycleEvent(); 246 CyclesLeft--; 247 if (!CyclesLeft) 248 Stage = IS_EXECUTED; 249 } 250 251 const unsigned WriteRef::INVALID_IID = std::numeric_limits<unsigned>::max(); 252 253 } // namespace mca 254 } // namespace llvm 255