1 | //===--------------------- Instruction.h ------------------------*- 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 | /// This file defines abstractions used by the Pipeline to model register reads, |
11 | /// register writes and instructions. |
12 | /// |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #ifndef LLVM_MCA_INSTRUCTION_H |
16 | #define LLVM_MCA_INSTRUCTION_H |
17 | |
18 | #include "llvm/ADT/ArrayRef.h" |
19 | #include "llvm/ADT/STLExtras.h" |
20 | #include "llvm/ADT/SmallVector.h" |
21 | #include "llvm/MC/MCRegister.h" // definition of MCPhysReg. |
22 | #include "llvm/Support/Compiler.h" |
23 | #include "llvm/Support/MathExtras.h" |
24 | |
25 | #ifndef NDEBUG |
26 | #include "llvm/Support/raw_ostream.h" |
27 | #endif |
28 | |
29 | #include <memory> |
30 | |
31 | namespace llvm { |
32 | |
33 | namespace mca { |
34 | |
35 | constexpr int UNKNOWN_CYCLES = -512; |
36 | |
37 | /// A representation of an mca::Instruction operand |
38 | /// for use in mca::CustomBehaviour. |
39 | class MCAOperand { |
40 | // This class is mostly copied from MCOperand within |
41 | // MCInst.h except that we don't keep track of |
42 | // expressions or sub-instructions. |
43 | enum MCAOperandType : unsigned char { |
44 | kInvalid, ///< Uninitialized, Relocatable immediate, or Sub-instruction. |
45 | kRegister, ///< Register operand. |
46 | kImmediate, ///< Immediate operand. |
47 | kSFPImmediate, ///< Single-floating-point immediate operand. |
48 | kDFPImmediate, ///< Double-Floating-point immediate operand. |
49 | }; |
50 | MCAOperandType Kind; |
51 | |
52 | union { |
53 | unsigned RegVal; |
54 | int64_t ImmVal; |
55 | uint32_t SFPImmVal; |
56 | uint64_t FPImmVal; |
57 | }; |
58 | |
59 | // We only store specific operands for specific instructions |
60 | // so an instruction's operand 3 may be stored within the list |
61 | // of MCAOperand as element 0. This Index attribute keeps track |
62 | // of the original index (3 for this example). |
63 | unsigned Index; |
64 | |
65 | public: |
66 | MCAOperand() : Kind(kInvalid), FPImmVal(), Index() {} |
67 | |
68 | bool isValid() const { return Kind != kInvalid; } |
69 | bool isReg() const { return Kind == kRegister; } |
70 | bool isImm() const { return Kind == kImmediate; } |
71 | bool isSFPImm() const { return Kind == kSFPImmediate; } |
72 | bool isDFPImm() const { return Kind == kDFPImmediate; } |
73 | |
74 | /// Returns the register number. |
75 | unsigned getReg() const { |
76 | assert(isReg() && "This is not a register operand!" ); |
77 | return RegVal; |
78 | } |
79 | |
80 | int64_t getImm() const { |
81 | assert(isImm() && "This is not an immediate" ); |
82 | return ImmVal; |
83 | } |
84 | |
85 | uint32_t getSFPImm() const { |
86 | assert(isSFPImm() && "This is not an SFP immediate" ); |
87 | return SFPImmVal; |
88 | } |
89 | |
90 | uint64_t getDFPImm() const { |
91 | assert(isDFPImm() && "This is not an FP immediate" ); |
92 | return FPImmVal; |
93 | } |
94 | |
95 | void setIndex(const unsigned Idx) { Index = Idx; } |
96 | |
97 | unsigned getIndex() const { return Index; } |
98 | |
99 | static MCAOperand createReg(unsigned Reg) { |
100 | MCAOperand Op; |
101 | Op.Kind = kRegister; |
102 | Op.RegVal = Reg; |
103 | return Op; |
104 | } |
105 | |
106 | static MCAOperand createImm(int64_t Val) { |
107 | MCAOperand Op; |
108 | Op.Kind = kImmediate; |
109 | Op.ImmVal = Val; |
110 | return Op; |
111 | } |
112 | |
113 | static MCAOperand createSFPImm(uint32_t Val) { |
114 | MCAOperand Op; |
115 | Op.Kind = kSFPImmediate; |
116 | Op.SFPImmVal = Val; |
117 | return Op; |
118 | } |
119 | |
120 | static MCAOperand createDFPImm(uint64_t Val) { |
121 | MCAOperand Op; |
122 | Op.Kind = kDFPImmediate; |
123 | Op.FPImmVal = Val; |
124 | return Op; |
125 | } |
126 | |
127 | static MCAOperand createInvalid() { |
128 | MCAOperand Op; |
129 | Op.Kind = kInvalid; |
130 | Op.FPImmVal = 0; |
131 | return Op; |
132 | } |
133 | }; |
134 | |
135 | /// A register write descriptor. |
136 | struct WriteDescriptor { |
137 | // Operand index. The index is negative for implicit writes only. |
138 | // For implicit writes, the actual operand index is computed performing |
139 | // a bitwise not of the OpIndex. |
140 | int OpIndex; |
141 | // Write latency. Number of cycles before write-back stage. |
142 | unsigned Latency; |
143 | // This field is set to a value different than zero only if this |
144 | // is an implicit definition. |
145 | MCPhysReg RegisterID; |
146 | // Instruction itineraries would set this field to the SchedClass ID. |
147 | // Otherwise, it defaults to the WriteResourceID from the MCWriteLatencyEntry |
148 | // element associated to this write. |
149 | // When computing read latencies, this value is matched against the |
150 | // "ReadAdvance" information. The hardware backend may implement |
151 | // dedicated forwarding paths to quickly propagate write results to dependent |
152 | // instructions waiting in the reservation station (effectively bypassing the |
153 | // write-back stage). |
154 | unsigned SClassOrWriteResourceID; |
155 | // True only if this is a write obtained from an optional definition. |
156 | // Optional definitions are allowed to reference regID zero (i.e. "no |
157 | // register"). |
158 | bool IsOptionalDef; |
159 | |
160 | bool isImplicitWrite() const { return OpIndex < 0; }; |
161 | }; |
162 | |
163 | /// A register read descriptor. |
164 | struct ReadDescriptor { |
165 | // A MCOperand index. This is used by the Dispatch logic to identify register |
166 | // reads. Implicit reads have negative indices. The actual operand index of an |
167 | // implicit read is the bitwise not of field OpIndex. |
168 | int OpIndex; |
169 | // The actual "UseIdx". This is used to query the ReadAdvance table. Explicit |
170 | // uses always come first in the sequence of uses. |
171 | unsigned UseIndex; |
172 | // This field is only set if this is an implicit read. |
173 | MCPhysReg RegisterID; |
174 | // Scheduling Class Index. It is used to query the scheduling model for the |
175 | // MCSchedClassDesc object. |
176 | unsigned SchedClassID; |
177 | |
178 | bool isImplicitRead() const { return OpIndex < 0; }; |
179 | }; |
180 | |
181 | class ReadState; |
182 | |
183 | /// A critical data dependency descriptor. |
184 | /// |
185 | /// Field RegID is set to the invalid register for memory dependencies. |
186 | struct CriticalDependency { |
187 | unsigned IID; |
188 | MCPhysReg RegID; |
189 | unsigned Cycles; |
190 | }; |
191 | |
192 | /// Tracks uses of a register definition (e.g. register write). |
193 | /// |
194 | /// Each implicit/explicit register write is associated with an instance of |
195 | /// this class. A WriteState object tracks the dependent users of a |
196 | /// register write. It also tracks how many cycles are left before the write |
197 | /// back stage. |
198 | class WriteState { |
199 | const WriteDescriptor *WD; |
200 | // On instruction issue, this field is set equal to the write latency. |
201 | // Before instruction issue, this field defaults to -512, a special |
202 | // value that represents an "unknown" number of cycles. |
203 | int CyclesLeft; |
204 | |
205 | // Actual register defined by this write. This field is only used |
206 | // to speedup queries on the register file. |
207 | // For implicit writes, this field always matches the value of |
208 | // field RegisterID from WD. |
209 | MCPhysReg RegisterID; |
210 | |
211 | // Physical register file that serves register RegisterID. |
212 | unsigned PRFID; |
213 | |
214 | // True if this write implicitly clears the upper portion of RegisterID's |
215 | // super-registers. |
216 | bool ; |
217 | |
218 | // True if this write is from a dependency breaking zero-idiom instruction. |
219 | bool WritesZero; |
220 | |
221 | // True if this write has been eliminated at register renaming stage. |
222 | // Example: a register move doesn't consume scheduler/pipleline resources if |
223 | // it is eliminated at register renaming stage. It still consumes |
224 | // decode bandwidth, and ROB entries. |
225 | bool IsEliminated; |
226 | |
227 | // This field is set if this is a partial register write, and it has a false |
228 | // dependency on any previous write of the same register (or a portion of it). |
229 | // DependentWrite must be able to complete before this write completes, so |
230 | // that we don't break the WAW, and the two writes can be merged together. |
231 | const WriteState *DependentWrite; |
232 | |
233 | // A partial write that is in a false dependency with this write. |
234 | WriteState *PartialWrite; |
235 | unsigned DependentWriteCyclesLeft; |
236 | |
237 | // Critical register dependency for this write. |
238 | CriticalDependency CRD; |
239 | |
240 | // A list of dependent reads. Users is a set of dependent |
241 | // reads. A dependent read is added to the set only if CyclesLeft |
242 | // is "unknown". As soon as CyclesLeft is 'known', each user in the set |
243 | // gets notified with the actual CyclesLeft. |
244 | |
245 | // The 'second' element of a pair is a "ReadAdvance" number of cycles. |
246 | SmallVector<std::pair<ReadState *, int>, 4> Users; |
247 | |
248 | public: |
249 | WriteState(const WriteDescriptor &Desc, MCPhysReg RegID, |
250 | bool = false, bool writesZero = false) |
251 | : WD(&Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID), PRFID(0), |
252 | ClearsSuperRegs(clearsSuperRegs), WritesZero(writesZero), |
253 | IsEliminated(false), DependentWrite(nullptr), PartialWrite(nullptr), |
254 | DependentWriteCyclesLeft(0), CRD() {} |
255 | |
256 | WriteState(const WriteState &Other) = default; |
257 | WriteState &operator=(const WriteState &Other) = default; |
258 | |
259 | int getCyclesLeft() const { return CyclesLeft; } |
260 | unsigned getWriteResourceID() const { return WD->SClassOrWriteResourceID; } |
261 | MCPhysReg getRegisterID() const { return RegisterID; } |
262 | void setRegisterID(const MCPhysReg RegID) { RegisterID = RegID; } |
263 | unsigned getRegisterFileID() const { return PRFID; } |
264 | unsigned getLatency() const { return WD->Latency; } |
265 | unsigned getDependentWriteCyclesLeft() const { |
266 | return DependentWriteCyclesLeft; |
267 | } |
268 | const WriteState *getDependentWrite() const { return DependentWrite; } |
269 | const CriticalDependency &getCriticalRegDep() const { return CRD; } |
270 | |
271 | // This method adds Use to the set of data dependent reads. IID is the |
272 | // instruction identifier associated with this write. ReadAdvance is the |
273 | // number of cycles to subtract from the latency of this data dependency. |
274 | // Use is in a RAW dependency with this write. |
275 | LLVM_ABI void addUser(unsigned IID, ReadState *Use, int ReadAdvance); |
276 | |
277 | // Use is a younger register write that is in a false dependency with this |
278 | // write. IID is the instruction identifier associated with this write. |
279 | LLVM_ABI void addUser(unsigned IID, WriteState *Use); |
280 | |
281 | unsigned getNumUsers() const { |
282 | unsigned NumUsers = Users.size(); |
283 | if (PartialWrite) |
284 | ++NumUsers; |
285 | return NumUsers; |
286 | } |
287 | |
288 | bool () const { return ClearsSuperRegs; } |
289 | bool isWriteZero() const { return WritesZero; } |
290 | bool isEliminated() const { return IsEliminated; } |
291 | |
292 | bool isReady() const { |
293 | if (DependentWrite) |
294 | return false; |
295 | unsigned CyclesLeft = getDependentWriteCyclesLeft(); |
296 | return !CyclesLeft || CyclesLeft < getLatency(); |
297 | } |
298 | |
299 | bool isExecuted() const { |
300 | return CyclesLeft != UNKNOWN_CYCLES && CyclesLeft <= 0; |
301 | } |
302 | |
303 | void setDependentWrite(const WriteState *Other) { DependentWrite = Other; } |
304 | LLVM_ABI void writeStartEvent(unsigned IID, MCPhysReg RegID, unsigned Cycles); |
305 | void setWriteZero() { WritesZero = true; } |
306 | void setEliminated() { |
307 | assert(Users.empty() && "Write is in an inconsistent state." ); |
308 | CyclesLeft = 0; |
309 | IsEliminated = true; |
310 | } |
311 | |
312 | void setPRF(unsigned PRF) { PRFID = PRF; } |
313 | |
314 | // On every cycle, update CyclesLeft and notify dependent users. |
315 | LLVM_ABI void cycleEvent(); |
316 | LLVM_ABI void onInstructionIssued(unsigned IID); |
317 | |
318 | #ifndef NDEBUG |
319 | void dump() const; |
320 | #endif |
321 | }; |
322 | |
323 | /// Tracks register operand latency in cycles. |
324 | /// |
325 | /// A read may be dependent on more than one write. This occurs when some |
326 | /// writes only partially update the register associated to this read. |
327 | class ReadState { |
328 | const ReadDescriptor *RD; |
329 | // Physical register identified associated to this read. |
330 | MCPhysReg RegisterID; |
331 | // Physical register file that serves register RegisterID. |
332 | unsigned PRFID; |
333 | // Number of writes that contribute to the definition of RegisterID. |
334 | // In the absence of partial register updates, the number of DependentWrites |
335 | // cannot be more than one. |
336 | unsigned DependentWrites; |
337 | // Number of cycles left before RegisterID can be read. This value depends on |
338 | // the latency of all the dependent writes. It defaults to UNKNOWN_CYCLES. |
339 | // It gets set to the value of field TotalCycles only when the 'CyclesLeft' of |
340 | // every dependent write is known. |
341 | int CyclesLeft; |
342 | // This field is updated on every writeStartEvent(). When the number of |
343 | // dependent writes (i.e. field DependentWrite) is zero, this value is |
344 | // propagated to field CyclesLeft. |
345 | unsigned TotalCycles; |
346 | // Longest register dependency. |
347 | CriticalDependency CRD; |
348 | // This field is set to true only if there are no dependent writes, and |
349 | // there are no `CyclesLeft' to wait. |
350 | bool IsReady; |
351 | // True if this is a read from a known zero register. |
352 | bool IsZero; |
353 | // True if this register read is from a dependency-breaking instruction. |
354 | bool IndependentFromDef; |
355 | |
356 | public: |
357 | ReadState(const ReadDescriptor &Desc, MCPhysReg RegID) |
358 | : RD(&Desc), RegisterID(RegID), PRFID(0), DependentWrites(0), |
359 | CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), CRD(), IsReady(true), |
360 | IsZero(false), IndependentFromDef(false) {} |
361 | |
362 | const ReadDescriptor &getDescriptor() const { return *RD; } |
363 | unsigned getSchedClass() const { return RD->SchedClassID; } |
364 | MCPhysReg getRegisterID() const { return RegisterID; } |
365 | unsigned getRegisterFileID() const { return PRFID; } |
366 | const CriticalDependency &getCriticalRegDep() const { return CRD; } |
367 | |
368 | bool isPending() const { return !IndependentFromDef && CyclesLeft > 0; } |
369 | bool isReady() const { return IsReady; } |
370 | bool isImplicitRead() const { return RD->isImplicitRead(); } |
371 | |
372 | bool isIndependentFromDef() const { return IndependentFromDef; } |
373 | void setIndependentFromDef() { IndependentFromDef = true; } |
374 | |
375 | LLVM_ABI void cycleEvent(); |
376 | LLVM_ABI void writeStartEvent(unsigned IID, MCPhysReg RegID, unsigned Cycles); |
377 | void setDependentWrites(unsigned Writes) { |
378 | DependentWrites = Writes; |
379 | IsReady = !Writes; |
380 | } |
381 | |
382 | bool isReadZero() const { return IsZero; } |
383 | void setReadZero() { IsZero = true; } |
384 | void setPRF(unsigned ID) { PRFID = ID; } |
385 | }; |
386 | |
387 | /// A sequence of cycles. |
388 | /// |
389 | /// This class can be used as a building block to construct ranges of cycles. |
390 | class CycleSegment { |
391 | unsigned Begin; // Inclusive. |
392 | unsigned End; // Exclusive. |
393 | bool Reserved; // Resources associated to this segment must be reserved. |
394 | |
395 | public: |
396 | CycleSegment(unsigned StartCycle, unsigned EndCycle, bool IsReserved = false) |
397 | : Begin(StartCycle), End(EndCycle), Reserved(IsReserved) {} |
398 | |
399 | bool contains(unsigned Cycle) const { return Cycle >= Begin && Cycle < End; } |
400 | bool startsAfter(const CycleSegment &CS) const { return End <= CS.Begin; } |
401 | bool endsBefore(const CycleSegment &CS) const { return Begin >= CS.End; } |
402 | bool overlaps(const CycleSegment &CS) const { |
403 | return !startsAfter(CS) && !endsBefore(CS); |
404 | } |
405 | bool isExecuting() const { return Begin == 0 && End != 0; } |
406 | bool isExecuted() const { return End == 0; } |
407 | bool operator<(const CycleSegment &Other) const { |
408 | return Begin < Other.Begin; |
409 | } |
410 | CycleSegment &operator--() { |
411 | if (Begin) |
412 | Begin--; |
413 | if (End) |
414 | End--; |
415 | return *this; |
416 | } |
417 | |
418 | bool isValid() const { return Begin <= End; } |
419 | unsigned size() const { return End - Begin; }; |
420 | void subtract(unsigned Cycles) { |
421 | assert(End >= Cycles); |
422 | End -= Cycles; |
423 | } |
424 | |
425 | unsigned begin() const { return Begin; } |
426 | unsigned end() const { return End; } |
427 | void setEnd(unsigned NewEnd) { End = NewEnd; } |
428 | bool isReserved() const { return Reserved; } |
429 | void setReserved() { Reserved = true; } |
430 | }; |
431 | |
432 | /// Helper used by class InstrDesc to describe how hardware resources |
433 | /// are used. |
434 | /// |
435 | /// This class describes how many resource units of a specific resource kind |
436 | /// (and how many cycles) are "used" by an instruction. |
437 | struct ResourceUsage { |
438 | CycleSegment CS; |
439 | unsigned NumUnits; |
440 | ResourceUsage(CycleSegment Cycles, unsigned Units = 1) |
441 | : CS(Cycles), NumUnits(Units) {} |
442 | unsigned size() const { return CS.size(); } |
443 | bool isReserved() const { return CS.isReserved(); } |
444 | void setReserved() { CS.setReserved(); } |
445 | }; |
446 | |
447 | /// An instruction descriptor |
448 | struct InstrDesc { |
449 | SmallVector<WriteDescriptor, 2> Writes; // Implicit writes are at the end. |
450 | SmallVector<ReadDescriptor, 4> Reads; // Implicit reads are at the end. |
451 | |
452 | // For every resource used by an instruction of this kind, this vector |
453 | // reports the number of "consumed cycles". |
454 | SmallVector<std::pair<uint64_t, ResourceUsage>, 4> Resources; |
455 | |
456 | // A bitmask of used hardware buffers. |
457 | uint64_t UsedBuffers; |
458 | |
459 | // A bitmask of used processor resource units. |
460 | uint64_t UsedProcResUnits; |
461 | |
462 | // A bitmask of used processor resource groups. |
463 | uint64_t UsedProcResGroups; |
464 | |
465 | unsigned MaxLatency; |
466 | // Number of MicroOps for this instruction. |
467 | unsigned NumMicroOps; |
468 | // SchedClassID used to construct this InstrDesc. |
469 | // This information is currently used by views to do fast queries on the |
470 | // subtarget when computing the reciprocal throughput. |
471 | unsigned SchedClassID; |
472 | |
473 | // True if all buffered resources are in-order, and there is at least one |
474 | // buffer which is a dispatch hazard (BufferSize = 0). |
475 | unsigned MustIssueImmediately : 1; |
476 | |
477 | // True if the corresponding mca::Instruction can be recycled. Currently only |
478 | // instructions that are neither variadic nor have any variant can be |
479 | // recycled. |
480 | unsigned IsRecyclable : 1; |
481 | |
482 | // True if some of the consumed group resources are partially overlapping. |
483 | unsigned HasPartiallyOverlappingGroups : 1; |
484 | |
485 | // A zero latency instruction doesn't consume any scheduler resources. |
486 | bool isZeroLatency() const { return !MaxLatency && Resources.empty(); } |
487 | |
488 | InstrDesc() = default; |
489 | InstrDesc(const InstrDesc &Other) = delete; |
490 | InstrDesc &operator=(const InstrDesc &Other) = delete; |
491 | }; |
492 | |
493 | /// Base class for instructions consumed by the simulation pipeline. |
494 | /// |
495 | /// This class tracks data dependencies as well as generic properties |
496 | /// of the instruction. |
497 | class InstructionBase { |
498 | const InstrDesc &Desc; |
499 | |
500 | // This field is set for instructions that are candidates for move |
501 | // elimination. For more information about move elimination, see the |
502 | // definition of RegisterMappingTracker in RegisterFile.h |
503 | bool IsOptimizableMove; |
504 | |
505 | // Output dependencies. |
506 | // One entry per each implicit and explicit register definition. |
507 | SmallVector<WriteState, 2> Defs; |
508 | |
509 | // Input dependencies. |
510 | // One entry per each implicit and explicit register use. |
511 | SmallVector<ReadState, 4> Uses; |
512 | |
513 | // List of operands which can be used by mca::CustomBehaviour |
514 | std::vector<MCAOperand> Operands; |
515 | |
516 | // Instruction opcode which can be used by mca::CustomBehaviour |
517 | unsigned Opcode; |
518 | |
519 | // Flags used by the LSUnit. |
520 | bool IsALoadBarrier : 1; |
521 | bool IsAStoreBarrier : 1; |
522 | // Flags copied from the InstrDesc and potentially modified by |
523 | // CustomBehaviour or (more likely) InstrPostProcess. |
524 | bool MayLoad : 1; |
525 | bool MayStore : 1; |
526 | bool HasSideEffects : 1; |
527 | bool BeginGroup : 1; |
528 | bool EndGroup : 1; |
529 | bool RetireOOO : 1; |
530 | |
531 | public: |
532 | InstructionBase(const InstrDesc &D, const unsigned Opcode) |
533 | : Desc(D), IsOptimizableMove(false), Operands(0), Opcode(Opcode), |
534 | IsALoadBarrier(false), IsAStoreBarrier(false) {} |
535 | |
536 | SmallVectorImpl<WriteState> &getDefs() { return Defs; } |
537 | ArrayRef<WriteState> getDefs() const { return Defs; } |
538 | SmallVectorImpl<ReadState> &getUses() { return Uses; } |
539 | ArrayRef<ReadState> getUses() const { return Uses; } |
540 | const InstrDesc &getDesc() const { return Desc; } |
541 | |
542 | unsigned getLatency() const { return Desc.MaxLatency; } |
543 | unsigned getNumMicroOps() const { return Desc.NumMicroOps; } |
544 | unsigned getOpcode() const { return Opcode; } |
545 | bool isALoadBarrier() const { return IsALoadBarrier; } |
546 | bool isAStoreBarrier() const { return IsAStoreBarrier; } |
547 | void setLoadBarrier(bool IsBarrier) { IsALoadBarrier = IsBarrier; } |
548 | void setStoreBarrier(bool IsBarrier) { IsAStoreBarrier = IsBarrier; } |
549 | |
550 | /// Return the MCAOperand which corresponds to index Idx within the original |
551 | /// MCInst. |
552 | const MCAOperand *getOperand(const unsigned Idx) const { |
553 | auto It = llvm::find_if(Range: Operands, P: [&Idx](const MCAOperand &Op) { |
554 | return Op.getIndex() == Idx; |
555 | }); |
556 | if (It == Operands.end()) |
557 | return nullptr; |
558 | return &(*It); |
559 | } |
560 | unsigned getNumOperands() const { return Operands.size(); } |
561 | void addOperand(const MCAOperand Op) { Operands.push_back(x: Op); } |
562 | |
563 | bool hasDependentUsers() const { |
564 | return any_of(Range: Defs, |
565 | P: [](const WriteState &Def) { return Def.getNumUsers() > 0; }); |
566 | } |
567 | |
568 | unsigned getNumUsers() const { |
569 | unsigned NumUsers = 0; |
570 | for (const WriteState &Def : Defs) |
571 | NumUsers += Def.getNumUsers(); |
572 | return NumUsers; |
573 | } |
574 | |
575 | // Returns true if this instruction is a candidate for move elimination. |
576 | bool isOptimizableMove() const { return IsOptimizableMove; } |
577 | void setOptimizableMove() { IsOptimizableMove = true; } |
578 | void clearOptimizableMove() { IsOptimizableMove = false; } |
579 | bool isMemOp() const { return MayLoad || MayStore; } |
580 | |
581 | // Getters and setters for general instruction flags. |
582 | void setMayLoad(bool newVal) { MayLoad = newVal; } |
583 | void setMayStore(bool newVal) { MayStore = newVal; } |
584 | void setHasSideEffects(bool newVal) { HasSideEffects = newVal; } |
585 | void setBeginGroup(bool newVal) { BeginGroup = newVal; } |
586 | void setEndGroup(bool newVal) { EndGroup = newVal; } |
587 | void setRetireOOO(bool newVal) { RetireOOO = newVal; } |
588 | |
589 | bool getMayLoad() const { return MayLoad; } |
590 | bool getMayStore() const { return MayStore; } |
591 | bool getHasSideEffects() const { return HasSideEffects; } |
592 | bool getBeginGroup() const { return BeginGroup; } |
593 | bool getEndGroup() const { return EndGroup; } |
594 | bool getRetireOOO() const { return RetireOOO; } |
595 | }; |
596 | |
597 | /// An instruction propagated through the simulated instruction pipeline. |
598 | /// |
599 | /// This class is used to monitor changes to the internal state of instructions |
600 | /// that are sent to the various components of the simulated hardware pipeline. |
601 | class Instruction : public InstructionBase { |
602 | enum InstrStage { |
603 | IS_INVALID, // Instruction in an invalid state. |
604 | IS_DISPATCHED, // Instruction dispatched but operands are not ready. |
605 | IS_PENDING, // Instruction is not ready, but operand latency is known. |
606 | IS_READY, // Instruction dispatched and operands ready. |
607 | IS_EXECUTING, // Instruction issued. |
608 | IS_EXECUTED, // Instruction executed. Values are written back. |
609 | IS_RETIRED // Instruction retired. |
610 | }; |
611 | |
612 | // The current instruction stage. |
613 | enum InstrStage Stage; |
614 | |
615 | // This value defaults to the instruction latency. This instruction is |
616 | // considered executed when field CyclesLeft goes to zero. |
617 | int CyclesLeft; |
618 | |
619 | // Retire Unit token ID for this instruction. |
620 | unsigned RCUTokenID; |
621 | |
622 | // LS token ID for this instruction. |
623 | // This field is set to the invalid null token if this is not a memory |
624 | // operation. |
625 | unsigned LSUTokenID; |
626 | |
627 | // A resource mask which identifies buffered resources consumed by this |
628 | // instruction at dispatch stage. In the absence of macro-fusion, this value |
629 | // should always match the value of field `UsedBuffers` from the instruction |
630 | // descriptor (see field InstrBase::Desc). |
631 | uint64_t UsedBuffers; |
632 | |
633 | // Critical register dependency. |
634 | CriticalDependency CriticalRegDep; |
635 | |
636 | // Critical memory dependency. |
637 | CriticalDependency CriticalMemDep; |
638 | |
639 | // A bitmask of busy processor resource units. |
640 | // This field is set to zero only if execution is not delayed during this |
641 | // cycle because of unavailable pipeline resources. |
642 | uint64_t CriticalResourceMask; |
643 | |
644 | // True if this instruction has been optimized at register renaming stage. |
645 | bool IsEliminated; |
646 | |
647 | public: |
648 | Instruction(const InstrDesc &D, const unsigned Opcode) |
649 | : InstructionBase(D, Opcode), Stage(IS_INVALID), |
650 | CyclesLeft(UNKNOWN_CYCLES), RCUTokenID(0), LSUTokenID(0), |
651 | UsedBuffers(D.UsedBuffers), CriticalRegDep(), CriticalMemDep(), |
652 | CriticalResourceMask(0), IsEliminated(false) {} |
653 | |
654 | LLVM_ABI void reset(); |
655 | |
656 | unsigned getRCUTokenID() const { return RCUTokenID; } |
657 | unsigned getLSUTokenID() const { return LSUTokenID; } |
658 | void setLSUTokenID(unsigned LSUTok) { LSUTokenID = LSUTok; } |
659 | |
660 | uint64_t getUsedBuffers() const { return UsedBuffers; } |
661 | void setUsedBuffers(uint64_t Mask) { UsedBuffers = Mask; } |
662 | void clearUsedBuffers() { UsedBuffers = 0ULL; } |
663 | |
664 | int getCyclesLeft() const { return CyclesLeft; } |
665 | |
666 | // Transition to the dispatch stage, and assign a RCUToken to this |
667 | // instruction. The RCUToken is used to track the completion of every |
668 | // register write performed by this instruction. |
669 | LLVM_ABI void dispatch(unsigned RCUTokenID); |
670 | |
671 | // Instruction issued. Transition to the IS_EXECUTING state, and update |
672 | // all the register definitions. |
673 | LLVM_ABI void execute(unsigned IID); |
674 | |
675 | // Force a transition from the IS_DISPATCHED state to the IS_READY or |
676 | // IS_PENDING state. State transitions normally occur either at the beginning |
677 | // of a new cycle (see method cycleEvent()), or as a result of another issue |
678 | // event. This method is called every time the instruction might have changed |
679 | // in state. It internally delegates to method updateDispatched() and |
680 | // updateWaiting(). |
681 | LLVM_ABI void update(); |
682 | LLVM_ABI bool updateDispatched(); |
683 | LLVM_ABI bool updatePending(); |
684 | |
685 | bool isInvalid() const { return Stage == IS_INVALID; } |
686 | bool isDispatched() const { return Stage == IS_DISPATCHED; } |
687 | bool isPending() const { return Stage == IS_PENDING; } |
688 | bool isReady() const { return Stage == IS_READY; } |
689 | bool isExecuting() const { return Stage == IS_EXECUTING; } |
690 | bool isExecuted() const { return Stage == IS_EXECUTED; } |
691 | bool isRetired() const { return Stage == IS_RETIRED; } |
692 | bool isEliminated() const { return IsEliminated; } |
693 | |
694 | // Forces a transition from state IS_DISPATCHED to state IS_EXECUTED. |
695 | LLVM_ABI void forceExecuted(); |
696 | void setEliminated() { IsEliminated = true; } |
697 | |
698 | void retire() { |
699 | assert(isExecuted() && "Instruction is in an invalid state!" ); |
700 | Stage = IS_RETIRED; |
701 | } |
702 | |
703 | const CriticalDependency &getCriticalRegDep() const { return CriticalRegDep; } |
704 | const CriticalDependency &getCriticalMemDep() const { return CriticalMemDep; } |
705 | LLVM_ABI const CriticalDependency &computeCriticalRegDep(); |
706 | void setCriticalMemDep(const CriticalDependency &MemDep) { |
707 | CriticalMemDep = MemDep; |
708 | } |
709 | |
710 | uint64_t getCriticalResourceMask() const { return CriticalResourceMask; } |
711 | void setCriticalResourceMask(uint64_t ResourceMask) { |
712 | CriticalResourceMask = ResourceMask; |
713 | } |
714 | |
715 | LLVM_ABI void cycleEvent(); |
716 | }; |
717 | |
718 | /// An InstRef contains both a SourceMgr index and Instruction pair. The index |
719 | /// is used as a unique identifier for the instruction. MCA will make use of |
720 | /// this index as a key throughout MCA. |
721 | class InstRef { |
722 | std::pair<unsigned, Instruction *> Data; |
723 | |
724 | public: |
725 | InstRef() : Data(std::make_pair(x: 0, y: nullptr)) {} |
726 | InstRef(unsigned Index, Instruction *I) : Data(std::make_pair(x&: Index, y&: I)) {} |
727 | |
728 | bool operator==(const InstRef &Other) const { return Data == Other.Data; } |
729 | bool operator!=(const InstRef &Other) const { return Data != Other.Data; } |
730 | bool operator<(const InstRef &Other) const { |
731 | return Data.first < Other.Data.first; |
732 | } |
733 | |
734 | unsigned getSourceIndex() const { return Data.first; } |
735 | Instruction *getInstruction() { return Data.second; } |
736 | const Instruction *getInstruction() const { return Data.second; } |
737 | |
738 | /// Returns true if this references a valid instruction. |
739 | explicit operator bool() const { return Data.second != nullptr; } |
740 | |
741 | /// Invalidate this reference. |
742 | void invalidate() { Data.second = nullptr; } |
743 | |
744 | #ifndef NDEBUG |
745 | void print(raw_ostream &OS) const { OS << getSourceIndex(); } |
746 | #endif |
747 | }; |
748 | |
749 | #ifndef NDEBUG |
750 | inline raw_ostream &operator<<(raw_ostream &OS, const InstRef &IR) { |
751 | IR.print(OS); |
752 | return OS; |
753 | } |
754 | #endif |
755 | |
756 | } // namespace mca |
757 | } // namespace llvm |
758 | |
759 | #endif // LLVM_MCA_INSTRUCTION_H |
760 | |