1 | //===---------------------- InOrderIssueStage.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 | /// \file |
9 | /// |
10 | /// InOrderIssueStage implements an in-order execution pipeline. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/MCA/Stages/InOrderIssueStage.h" |
15 | #include "llvm/MCA/HardwareUnits/LSUnit.h" |
16 | #include "llvm/MCA/HardwareUnits/RegisterFile.h" |
17 | #include "llvm/MCA/HardwareUnits/RetireControlUnit.h" |
18 | #include "llvm/MCA/Instruction.h" |
19 | |
20 | #define DEBUG_TYPE "llvm-mca" |
21 | namespace llvm { |
22 | namespace mca { |
23 | |
24 | void StallInfo::clear() { |
25 | IR.invalidate(); |
26 | CyclesLeft = 0; |
27 | Kind = StallKind::DEFAULT; |
28 | } |
29 | |
30 | void StallInfo::update(const InstRef &Inst, unsigned Cycles, StallKind SK) { |
31 | IR = Inst; |
32 | CyclesLeft = Cycles; |
33 | Kind = SK; |
34 | } |
35 | |
36 | void StallInfo::cycleEnd() { |
37 | if (!isValid()) |
38 | return; |
39 | |
40 | if (!CyclesLeft) |
41 | return; |
42 | |
43 | --CyclesLeft; |
44 | } |
45 | |
46 | InOrderIssueStage::InOrderIssueStage(const MCSubtargetInfo &STI, |
47 | RegisterFile &PRF, CustomBehaviour &CB, |
48 | LSUnit &LSU) |
49 | : STI(STI), PRF(PRF), RM(STI.getSchedModel()), CB(CB), LSU(LSU), |
50 | NumIssued(), CarryOver(), Bandwidth(), LastWriteBackCycle() {} |
51 | |
52 | unsigned InOrderIssueStage::getIssueWidth() const { |
53 | return STI.getSchedModel().IssueWidth; |
54 | } |
55 | |
56 | bool InOrderIssueStage::hasWorkToComplete() const { |
57 | return !IssuedInst.empty() || SI.isValid() || CarriedOver; |
58 | } |
59 | |
60 | bool InOrderIssueStage::isAvailable(const InstRef &IR) const { |
61 | if (SI.isValid() || CarriedOver) |
62 | return false; |
63 | |
64 | const Instruction &Inst = *IR.getInstruction(); |
65 | unsigned NumMicroOps = Inst.getNumMicroOps(); |
66 | |
67 | bool ShouldCarryOver = NumMicroOps > getIssueWidth(); |
68 | if (Bandwidth < NumMicroOps && !ShouldCarryOver) |
69 | return false; |
70 | |
71 | // Instruction with BeginGroup must be the first instruction to be issued in a |
72 | // cycle. |
73 | if (Inst.getBeginGroup() && NumIssued != 0) |
74 | return false; |
75 | |
76 | return true; |
77 | } |
78 | |
79 | static bool hasResourceHazard(const ResourceManager &RM, const InstRef &IR) { |
80 | if (RM.checkAvailability(Desc: IR.getInstruction()->getDesc())) { |
81 | LLVM_DEBUG(dbgs() << "[E] Stall #" << IR << '\n'); |
82 | return true; |
83 | } |
84 | |
85 | return false; |
86 | } |
87 | |
88 | static unsigned findFirstWriteBackCycle(const InstRef &IR) { |
89 | unsigned FirstWBCycle = IR.getInstruction()->getLatency(); |
90 | for (const WriteState &WS : IR.getInstruction()->getDefs()) { |
91 | int CyclesLeft = WS.getCyclesLeft(); |
92 | if (CyclesLeft == UNKNOWN_CYCLES) |
93 | CyclesLeft = WS.getLatency(); |
94 | if (CyclesLeft < 0) |
95 | CyclesLeft = 0; |
96 | FirstWBCycle = std::min(a: FirstWBCycle, b: (unsigned)CyclesLeft); |
97 | } |
98 | return FirstWBCycle; |
99 | } |
100 | |
101 | /// Return a number of cycles left until register requirements of the |
102 | /// instructions are met. |
103 | static unsigned checkRegisterHazard(const RegisterFile &PRF, |
104 | const MCSubtargetInfo &STI, |
105 | const InstRef &IR) { |
106 | for (const ReadState &RS : IR.getInstruction()->getUses()) { |
107 | RegisterFile::RAWHazard Hazard = PRF.checkRAWHazards(STI, RS); |
108 | if (Hazard.isValid()) |
109 | return Hazard.hasUnknownCycles() ? 1U : Hazard.CyclesLeft; |
110 | } |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | bool InOrderIssueStage::canExecute(const InstRef &IR) { |
116 | assert(!SI.getCyclesLeft() && "Should not have reached this code!" ); |
117 | assert(!SI.isValid() && "Should not have reached this code!" ); |
118 | |
119 | if (unsigned Cycles = checkRegisterHazard(PRF, STI, IR)) { |
120 | SI.update(Inst: IR, Cycles, SK: StallInfo::StallKind::REGISTER_DEPS); |
121 | return false; |
122 | } |
123 | |
124 | if (hasResourceHazard(RM, IR)) { |
125 | SI.update(Inst: IR, /* delay */ Cycles: 1, SK: StallInfo::StallKind::DISPATCH); |
126 | return false; |
127 | } |
128 | |
129 | if (IR.getInstruction()->isMemOp() && !LSU.isReady(IR)) { |
130 | // This load (store) aliases with a preceding store (load). Delay |
131 | // it until the depenency is cleared. |
132 | SI.update(Inst: IR, /* delay */ Cycles: 1, SK: StallInfo::StallKind::LOAD_STORE); |
133 | return false; |
134 | } |
135 | |
136 | if (unsigned CustomStallCycles = CB.checkCustomHazard(IssuedInst, IR)) { |
137 | SI.update(Inst: IR, Cycles: CustomStallCycles, SK: StallInfo::StallKind::CUSTOM_STALL); |
138 | return false; |
139 | } |
140 | |
141 | if (LastWriteBackCycle) { |
142 | if (!IR.getInstruction()->getRetireOOO()) { |
143 | unsigned NextWriteBackCycle = findFirstWriteBackCycle(IR); |
144 | // Delay the instruction to ensure that writes happen in program order. |
145 | if (NextWriteBackCycle < LastWriteBackCycle) { |
146 | SI.update(Inst: IR, Cycles: LastWriteBackCycle - NextWriteBackCycle, |
147 | SK: StallInfo::StallKind::DELAY); |
148 | return false; |
149 | } |
150 | } |
151 | } |
152 | |
153 | return true; |
154 | } |
155 | |
156 | static void addRegisterReadWrite(RegisterFile &PRF, Instruction &IS, |
157 | unsigned SourceIndex, |
158 | const MCSubtargetInfo &STI, |
159 | SmallVectorImpl<unsigned> &UsedRegs) { |
160 | assert(!IS.isEliminated()); |
161 | |
162 | for (ReadState &RS : IS.getUses()) |
163 | PRF.addRegisterRead(RS, STI); |
164 | |
165 | for (WriteState &WS : IS.getDefs()) |
166 | PRF.addRegisterWrite(Write: WriteRef(SourceIndex, &WS), UsedPhysRegs: UsedRegs); |
167 | } |
168 | |
169 | void InOrderIssueStage::notifyInstructionIssued(const InstRef &IR, |
170 | ArrayRef<ResourceUse> UsedRes) { |
171 | notifyEvent<HWInstructionEvent>( |
172 | Event: HWInstructionEvent(HWInstructionEvent::Ready, IR)); |
173 | notifyEvent<HWInstructionEvent>(Event: HWInstructionIssuedEvent(IR, UsedRes)); |
174 | |
175 | LLVM_DEBUG(dbgs() << "[E] Issued #" << IR << "\n" ); |
176 | } |
177 | |
178 | void InOrderIssueStage::notifyInstructionDispatched( |
179 | const InstRef &IR, unsigned Ops, ArrayRef<unsigned> UsedRegs) { |
180 | notifyEvent<HWInstructionEvent>( |
181 | Event: HWInstructionDispatchedEvent(IR, UsedRegs, Ops)); |
182 | |
183 | LLVM_DEBUG(dbgs() << "[E] Dispatched #" << IR << "\n" ); |
184 | } |
185 | |
186 | void InOrderIssueStage::notifyInstructionExecuted(const InstRef &IR) { |
187 | notifyEvent<HWInstructionEvent>( |
188 | Event: HWInstructionEvent(HWInstructionEvent::Executed, IR)); |
189 | LLVM_DEBUG(dbgs() << "[E] Instruction #" << IR << " is executed\n" ); |
190 | } |
191 | |
192 | void InOrderIssueStage::notifyInstructionRetired(const InstRef &IR, |
193 | ArrayRef<unsigned> FreedRegs) { |
194 | notifyEvent<HWInstructionEvent>(Event: HWInstructionRetiredEvent(IR, FreedRegs)); |
195 | LLVM_DEBUG(dbgs() << "[E] Retired #" << IR << " \n" ); |
196 | } |
197 | |
198 | llvm::Error InOrderIssueStage::execute(InstRef &IR) { |
199 | Instruction &IS = *IR.getInstruction(); |
200 | if (IS.isMemOp()) |
201 | IS.setLSUTokenID(LSU.dispatch(IR)); |
202 | |
203 | if (llvm::Error E = tryIssue(IR)) |
204 | return E; |
205 | |
206 | if (SI.isValid()) |
207 | notifyStallEvent(); |
208 | |
209 | return llvm::ErrorSuccess(); |
210 | } |
211 | |
212 | llvm::Error InOrderIssueStage::tryIssue(InstRef &IR) { |
213 | Instruction &IS = *IR.getInstruction(); |
214 | unsigned SourceIndex = IR.getSourceIndex(); |
215 | const InstrDesc &Desc = IS.getDesc(); |
216 | |
217 | if (!canExecute(IR)) { |
218 | LLVM_DEBUG(dbgs() << "[N] Stalled #" << SI.getInstruction() << " for " |
219 | << SI.getCyclesLeft() << " cycles\n" ); |
220 | Bandwidth = 0; |
221 | return llvm::ErrorSuccess(); |
222 | } |
223 | |
224 | unsigned RCUTokenID = RetireControlUnit::UnhandledTokenID; |
225 | IS.dispatch(RCUTokenID); |
226 | |
227 | SmallVector<unsigned, 4> UsedRegs(PRF.getNumRegisterFiles()); |
228 | addRegisterReadWrite(PRF, IS, SourceIndex, STI, UsedRegs); |
229 | |
230 | unsigned NumMicroOps = IS.getNumMicroOps(); |
231 | notifyInstructionDispatched(IR, Ops: NumMicroOps, UsedRegs); |
232 | |
233 | SmallVector<ResourceUse, 4> UsedResources; |
234 | RM.issueInstruction(Desc, Pipes&: UsedResources); |
235 | IS.execute(IID: SourceIndex); |
236 | |
237 | if (IS.isMemOp()) |
238 | LSU.onInstructionIssued(IR); |
239 | |
240 | // Replace resource masks with valid resource processor IDs. |
241 | for (ResourceUse &Use : UsedResources) { |
242 | uint64_t Mask = Use.first.first; |
243 | Use.first.first = RM.resolveResourceMask(Mask); |
244 | } |
245 | notifyInstructionIssued(IR, UsedRes: UsedResources); |
246 | |
247 | bool ShouldCarryOver = NumMicroOps > Bandwidth; |
248 | if (ShouldCarryOver) { |
249 | CarryOver = NumMicroOps - Bandwidth; |
250 | CarriedOver = IR; |
251 | Bandwidth = 0; |
252 | NumIssued += Bandwidth; |
253 | LLVM_DEBUG(dbgs() << "[N] Carry over #" << IR << " \n" ); |
254 | } else { |
255 | NumIssued += NumMicroOps; |
256 | Bandwidth = IS.getEndGroup() ? 0 : Bandwidth - NumMicroOps; |
257 | } |
258 | |
259 | // If the instruction has a latency of 0, we need to handle |
260 | // the execution and retirement now. If the instruction is issued in multiple |
261 | // cycles, we cannot handle the instruction being executed here so we make |
262 | // updateCarriedOver responsible. |
263 | if (IS.isExecuted() && !ShouldCarryOver) { |
264 | PRF.onInstructionExecuted(IS: &IS); |
265 | LSU.onInstructionExecuted(IR); |
266 | notifyInstructionExecuted(IR); |
267 | |
268 | retireInstruction(IR); |
269 | return llvm::ErrorSuccess(); |
270 | } |
271 | |
272 | IssuedInst.push_back(Elt: IR); |
273 | |
274 | if (!IR.getInstruction()->getRetireOOO()) |
275 | LastWriteBackCycle = IS.getCyclesLeft(); |
276 | |
277 | return llvm::ErrorSuccess(); |
278 | } |
279 | |
280 | void InOrderIssueStage::updateIssuedInst() { |
281 | // Update other instructions. Executed instructions will be retired during the |
282 | // next cycle. |
283 | unsigned NumExecuted = 0; |
284 | for (auto I = IssuedInst.begin(), E = IssuedInst.end(); |
285 | I != (E - NumExecuted);) { |
286 | InstRef &IR = *I; |
287 | Instruction &IS = *IR.getInstruction(); |
288 | |
289 | IS.cycleEvent(); |
290 | if (!IS.isExecuted()) { |
291 | LLVM_DEBUG(dbgs() << "[N] Instruction #" << IR |
292 | << " is still executing\n" ); |
293 | ++I; |
294 | continue; |
295 | } |
296 | |
297 | // If the instruction takes multiple cycles to issue, defer these calls |
298 | // to updateCarriedOver. We still remove from IssuedInst even if there is |
299 | // carry over to avoid an extra call to cycleEvent in the next cycle. |
300 | if (!CarriedOver) { |
301 | PRF.onInstructionExecuted(IS: &IS); |
302 | LSU.onInstructionExecuted(IR); |
303 | notifyInstructionExecuted(IR); |
304 | |
305 | retireInstruction(IR&: *I); |
306 | } |
307 | |
308 | ++NumExecuted; |
309 | |
310 | std::iter_swap(I, E - NumExecuted); |
311 | } |
312 | |
313 | if (NumExecuted) |
314 | IssuedInst.resize(N: IssuedInst.size() - NumExecuted); |
315 | } |
316 | |
317 | void InOrderIssueStage::updateCarriedOver() { |
318 | if (!CarriedOver) |
319 | return; |
320 | |
321 | assert(!SI.isValid() && "A stalled instruction cannot be carried over." ); |
322 | |
323 | if (CarryOver > Bandwidth) { |
324 | CarryOver -= Bandwidth; |
325 | Bandwidth = 0; |
326 | LLVM_DEBUG(dbgs() << "[N] Carry over (" << CarryOver << "uops left) #" |
327 | << CarriedOver << " \n" ); |
328 | return; |
329 | } |
330 | |
331 | LLVM_DEBUG(dbgs() << "[N] Carry over (complete) #" << CarriedOver << " \n" ); |
332 | |
333 | if (CarriedOver.getInstruction()->getEndGroup()) |
334 | Bandwidth = 0; |
335 | else |
336 | Bandwidth -= CarryOver; |
337 | |
338 | // updateIssuedInst defered these calls to updateCarriedOver when there was |
339 | // a carry over. |
340 | if (CarriedOver.getInstruction()->isExecuted()) { |
341 | PRF.onInstructionExecuted(IS: CarriedOver.getInstruction()); |
342 | LSU.onInstructionExecuted(IR: CarriedOver); |
343 | notifyInstructionExecuted(IR: CarriedOver); |
344 | |
345 | retireInstruction(IR&: CarriedOver); |
346 | } |
347 | |
348 | CarriedOver = InstRef(); |
349 | CarryOver = 0; |
350 | } |
351 | |
352 | void InOrderIssueStage::retireInstruction(InstRef &IR) { |
353 | Instruction &IS = *IR.getInstruction(); |
354 | IS.retire(); |
355 | |
356 | llvm::SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles()); |
357 | for (const WriteState &WS : IS.getDefs()) |
358 | PRF.removeRegisterWrite(WS, FreedPhysRegs: FreedRegs); |
359 | |
360 | if (IS.isMemOp()) |
361 | LSU.onInstructionRetired(IR); |
362 | |
363 | notifyInstructionRetired(IR, FreedRegs); |
364 | } |
365 | |
366 | void InOrderIssueStage::notifyStallEvent() { |
367 | assert(SI.getCyclesLeft() && "A zero cycles stall?" ); |
368 | assert(SI.isValid() && "Invalid stall information found!" ); |
369 | |
370 | const InstRef &IR = SI.getInstruction(); |
371 | |
372 | switch (SI.getStallKind()) { |
373 | default: |
374 | break; |
375 | case StallInfo::StallKind::REGISTER_DEPS: { |
376 | notifyEvent<HWStallEvent>( |
377 | Event: HWStallEvent(HWStallEvent::RegisterFileStall, IR)); |
378 | notifyEvent<HWPressureEvent>( |
379 | Event: HWPressureEvent(HWPressureEvent::REGISTER_DEPS, IR)); |
380 | break; |
381 | } |
382 | case StallInfo::StallKind::DISPATCH: { |
383 | notifyEvent<HWStallEvent>( |
384 | Event: HWStallEvent(HWStallEvent::DispatchGroupStall, IR)); |
385 | notifyEvent<HWPressureEvent>( |
386 | Event: HWPressureEvent(HWPressureEvent::RESOURCES, IR)); |
387 | break; |
388 | } |
389 | case StallInfo::StallKind::CUSTOM_STALL: { |
390 | notifyEvent<HWStallEvent>( |
391 | Event: HWStallEvent(HWStallEvent::CustomBehaviourStall, IR)); |
392 | break; |
393 | } |
394 | } |
395 | } |
396 | |
397 | llvm::Error InOrderIssueStage::cycleStart() { |
398 | NumIssued = 0; |
399 | Bandwidth = getIssueWidth(); |
400 | |
401 | PRF.cycleStart(); |
402 | LSU.cycleEvent(); |
403 | |
404 | // Release consumed resources. |
405 | SmallVector<ResourceRef, 4> Freed; |
406 | RM.cycleEvent(ResourcesFreed&: Freed); |
407 | |
408 | updateIssuedInst(); |
409 | |
410 | // Continue to issue the instruction carried over from the previous cycle |
411 | updateCarriedOver(); |
412 | |
413 | // Issue instructions scheduled for this cycle |
414 | if (SI.isValid()) { |
415 | if (!SI.getCyclesLeft()) { |
416 | // Make a copy of the reference, and try issue it again. |
417 | // Do not take the instruction reference because SI.clear() will |
418 | // invalidate it. |
419 | InstRef IR = SI.getInstruction(); |
420 | SI.clear(); |
421 | |
422 | if (llvm::Error E = tryIssue(IR)) |
423 | return E; |
424 | } |
425 | |
426 | if (SI.getCyclesLeft()) { |
427 | // The instruction is still stalled, cannot issue any new instructions in |
428 | // this cycle. |
429 | notifyStallEvent(); |
430 | Bandwidth = 0; |
431 | return llvm::ErrorSuccess(); |
432 | } |
433 | } |
434 | |
435 | assert((NumIssued <= getIssueWidth()) && "Overflow." ); |
436 | return llvm::ErrorSuccess(); |
437 | } |
438 | |
439 | llvm::Error InOrderIssueStage::cycleEnd() { |
440 | PRF.cycleEnd(); |
441 | SI.cycleEnd(); |
442 | |
443 | if (LastWriteBackCycle > 0) |
444 | --LastWriteBackCycle; |
445 | |
446 | return llvm::ErrorSuccess(); |
447 | } |
448 | |
449 | } // namespace mca |
450 | } // namespace llvm |
451 | |