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