Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 1 | //===--------------------- Instruction.h ------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | /// \file |
| 10 | /// |
| 11 | /// This file defines abstractions used by the Backend to model register reads, |
| 12 | /// register writes and instructions. |
| 13 | /// |
| 14 | //===----------------------------------------------------------------------===// |
| 15 | |
| 16 | #ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H |
| 17 | #define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H |
| 18 | |
| 19 | #include "llvm/Support/MathExtras.h" |
| 20 | #include <memory> |
| 21 | #include <set> |
| 22 | #include <vector> |
| 23 | |
| 24 | namespace mca { |
| 25 | |
| 26 | struct WriteDescriptor; |
| 27 | struct ReadDescriptor; |
| 28 | class WriteState; |
| 29 | class ReadState; |
| 30 | |
| 31 | constexpr int UNKNOWN_CYCLES = -512; |
| 32 | |
| 33 | /// \brief A register write descriptor. |
| 34 | struct WriteDescriptor { |
| 35 | int OpIndex; // Operand index. -1 if this is an implicit write. |
| 36 | // Write latency. Number of cycles before write-back stage. |
| 37 | int Latency; |
| 38 | // This field is set to a value different than zero only if this |
| 39 | // is an implicit definition. |
| 40 | unsigned RegisterID; |
| 41 | // True if this write generates a partial update of a super-registers. |
| 42 | // On X86, this flag is set by byte/word writes on GPR registers. Also, |
| 43 | // a write of an XMM register only partially updates the corresponding |
| 44 | // YMM super-register if the write is associated to a legacy SSE instruction. |
| 45 | bool FullyUpdatesSuperRegs; |
| 46 | // Instruction itineraries would set this field to the SchedClass ID. |
| 47 | // Otherwise, it defaults to the WriteResourceID from teh MCWriteLatencyEntry |
| 48 | // element associated to this write. |
| 49 | // When computing read latencies, this value is matched against the |
| 50 | // "ReadAdvance" information. The hardware backend may implement |
| 51 | // dedicated forwarding paths to quickly propagate write results to dependent |
| 52 | // instructions waiting in the reservation station (effectively bypassing the |
| 53 | // write-back stage). |
| 54 | unsigned SClassOrWriteResourceID; |
| 55 | // True only if this is a write obtained from an optional definition. |
| 56 | // Optional definitions are allowed to reference regID zero (i.e. "no |
| 57 | // register"). |
| 58 | bool IsOptionalDef; |
| 59 | }; |
| 60 | |
| 61 | /// \brief A register read descriptor. |
| 62 | struct ReadDescriptor { |
| 63 | // This field defaults to -1 if this is an implicit read. |
| 64 | int OpIndex; |
| 65 | // This field is only set if this is an implicit read. |
| 66 | unsigned RegisterID; |
| 67 | // Scheduling Class Index. It is used to query the scheduling model for the |
| 68 | // MCSchedClassDesc object. |
| 69 | unsigned SchedClassID; |
| 70 | // True if there may be a local forwarding logic in hardware to serve a |
| 71 | // write used by this read. This information, along with SchedClassID, is |
| 72 | // used to dynamically check at Instruction creation time, if the input |
| 73 | // operands can benefit from a ReadAdvance bonus. |
| 74 | bool HasReadAdvanceEntries; |
| 75 | }; |
| 76 | |
| 77 | /// \brief Tracks uses of a register definition (e.g. register write). |
| 78 | /// |
| 79 | /// Each implicit/explicit register write is associated with an instance of |
| 80 | /// this class. A WriteState object tracks the dependent users of a |
| 81 | /// register write. It also tracks how many cycles are left before the write |
| 82 | /// back stage. |
| 83 | class WriteState { |
| 84 | const WriteDescriptor &WD; |
| 85 | // On instruction issue, this field is set equal to the write latency. |
| 86 | // Before instruction issue, this field defaults to -512, a special |
| 87 | // value that represents an "unknown" number of cycles. |
| 88 | int CyclesLeft; |
| 89 | |
| 90 | // Actual register defined by this write. This field is only used |
| 91 | // to speedup queries on the register file. |
| 92 | // For implicit writes, this field always matches the value of |
| 93 | // field RegisterID from WD. |
| 94 | unsigned RegisterID; |
| 95 | |
| 96 | // A list of dependent reads. Users is a set of dependent |
| 97 | // reads. A dependent read is added to the set only if CyclesLeft |
| 98 | // is "unknown". As soon as CyclesLeft is 'known', each user in the set |
| 99 | // gets notified with the actual CyclesLeft. |
| 100 | |
| 101 | // The 'second' element of a pair is a "ReadAdvance" number of cycles. |
| 102 | std::set<std::pair<ReadState *, int>> Users; |
| 103 | |
| 104 | public: |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 105 | WriteState(const WriteDescriptor &Desc, unsigned RegID) |
| 106 | : WD(Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID) {} |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 107 | WriteState(const WriteState &Other) = delete; |
| 108 | WriteState &operator=(const WriteState &Other) = delete; |
| 109 | |
| 110 | int getCyclesLeft() const { return CyclesLeft; } |
| 111 | unsigned getWriteResourceID() const { return WD.SClassOrWriteResourceID; } |
| 112 | unsigned getRegisterID() const { return RegisterID; } |
| 113 | void setRegisterID(unsigned ID) { RegisterID = ID; } |
| 114 | |
| 115 | void addUser(ReadState *Use, int ReadAdvance); |
| 116 | bool fullyUpdatesSuperRegs() const { return WD.FullyUpdatesSuperRegs; } |
| 117 | bool isWrittenBack() const { return CyclesLeft == 0; } |
| 118 | |
| 119 | // On every cycle, update CyclesLeft and notify dependent users. |
| 120 | void cycleEvent(); |
| 121 | void onInstructionIssued(); |
| 122 | |
| 123 | #ifndef NDEBUG |
| 124 | void dump() const; |
| 125 | #endif |
| 126 | }; |
| 127 | |
| 128 | /// \brief Tracks register operand latency in cycles. |
| 129 | /// |
| 130 | /// A read may be dependent on more than one write. This occurs when some |
| 131 | /// writes only partially update the register associated to this read. |
| 132 | class ReadState { |
| 133 | const ReadDescriptor &RD; |
Andrea Di Biagio | 4732d43ca | 2018-03-14 14:57:23 +0000 | [diff] [blame] | 134 | unsigned RegisterID; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 135 | unsigned DependentWrites; |
| 136 | int CyclesLeft; |
| 137 | unsigned TotalCycles; |
| 138 | |
| 139 | public: |
| 140 | bool isReady() const { |
| 141 | if (DependentWrites) |
| 142 | return false; |
| 143 | return (CyclesLeft == UNKNOWN_CYCLES || CyclesLeft == 0); |
| 144 | } |
| 145 | |
Andrea Di Biagio | 4732d43ca | 2018-03-14 14:57:23 +0000 | [diff] [blame] | 146 | ReadState(const ReadDescriptor &Desc, unsigned RegID) |
| 147 | : RD(Desc), RegisterID(RegID), DependentWrites(0), |
| 148 | CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0) {} |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 149 | ReadState(const ReadState &Other) = delete; |
| 150 | ReadState &operator=(const ReadState &Other) = delete; |
| 151 | |
| 152 | const ReadDescriptor &getDescriptor() const { return RD; } |
| 153 | unsigned getSchedClass() const { return RD.SchedClassID; } |
Andrea Di Biagio | 4732d43ca | 2018-03-14 14:57:23 +0000 | [diff] [blame] | 154 | unsigned getRegisterID() const { return RegisterID; } |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 155 | void cycleEvent(); |
| 156 | void writeStartEvent(unsigned Cycles); |
| 157 | void setDependentWrites(unsigned Writes) { DependentWrites = Writes; } |
| 158 | }; |
| 159 | |
| 160 | /// \brief A sequence of cycles. |
| 161 | /// |
| 162 | /// This class can be used as a building block to construct ranges of cycles. |
| 163 | class CycleSegment { |
| 164 | unsigned Begin; // Inclusive. |
| 165 | unsigned End; // Exclusive. |
| 166 | bool Reserved; // Resources associated to this segment must be reserved. |
| 167 | |
| 168 | public: |
| 169 | CycleSegment(unsigned StartCycle, unsigned EndCycle, bool IsReserved = false) |
| 170 | : Begin(StartCycle), End(EndCycle), Reserved(IsReserved) {} |
| 171 | |
| 172 | bool contains(unsigned Cycle) const { return Cycle >= Begin && Cycle < End; } |
| 173 | bool startsAfter(const CycleSegment &CS) const { return End <= CS.Begin; } |
| 174 | bool endsBefore(const CycleSegment &CS) const { return Begin >= CS.End; } |
| 175 | bool overlaps(const CycleSegment &CS) const { |
| 176 | return !startsAfter(CS) && !endsBefore(CS); |
| 177 | } |
| 178 | bool isExecuting() const { return Begin == 0 && End != 0; } |
| 179 | bool isExecuted() const { return End == 0; } |
| 180 | bool operator<(const CycleSegment &Other) const { |
| 181 | return Begin < Other.Begin; |
| 182 | } |
| 183 | CycleSegment &operator--(void) { |
| 184 | if (Begin) |
| 185 | Begin--; |
| 186 | if (End) |
| 187 | End--; |
| 188 | return *this; |
| 189 | } |
| 190 | |
| 191 | bool isValid() const { return Begin <= End; } |
| 192 | unsigned size() const { return End - Begin; }; |
| 193 | void Subtract(unsigned Cycles) { |
| 194 | assert(End >= Cycles); |
| 195 | End -= Cycles; |
| 196 | } |
| 197 | |
| 198 | unsigned begin() const { return Begin; } |
| 199 | unsigned end() const { return End; } |
| 200 | void setEnd(unsigned NewEnd) { End = NewEnd; } |
| 201 | bool isReserved() const { return Reserved; } |
| 202 | void setReserved() { Reserved = true; } |
| 203 | }; |
| 204 | |
| 205 | /// \brief Helper used by class InstrDesc to describe how hardware resources |
| 206 | /// are used. |
| 207 | /// |
| 208 | /// This class describes how many resource units of a specific resource kind |
| 209 | /// (and how many cycles) are "used" by an instruction. |
| 210 | struct ResourceUsage { |
| 211 | CycleSegment CS; |
| 212 | unsigned NumUnits; |
| 213 | ResourceUsage(CycleSegment Cycles, unsigned Units = 1) |
| 214 | : CS(Cycles), NumUnits(Units) {} |
| 215 | unsigned size() const { return CS.size(); } |
| 216 | bool isReserved() const { return CS.isReserved(); } |
| 217 | void setReserved() { CS.setReserved(); } |
| 218 | }; |
| 219 | |
| 220 | /// \brief An instruction descriptor |
| 221 | struct InstrDesc { |
| 222 | std::vector<WriteDescriptor> Writes; // Implicit writes are at the end. |
| 223 | std::vector<ReadDescriptor> Reads; // Implicit reads are at the end. |
| 224 | |
| 225 | // For every resource used by an instruction of this kind, this vector |
| 226 | // reports the number of "consumed cycles". |
| 227 | std::vector<std::pair<uint64_t, ResourceUsage>> Resources; |
| 228 | |
| 229 | // A list of buffered resources consumed by this instruction. |
| 230 | std::vector<uint64_t> Buffers; |
| 231 | unsigned MaxLatency; |
| 232 | // Number of MicroOps for this instruction. |
| 233 | unsigned NumMicroOps; |
| 234 | |
| 235 | bool MayLoad; |
| 236 | bool MayStore; |
| 237 | bool HasSideEffects; |
| 238 | }; |
| 239 | |
| 240 | /// An instruction dispatched to the out-of-order backend. |
| 241 | /// |
| 242 | /// This class is used to monitor changes in the internal state of instructions |
| 243 | /// that are dispatched by the DispatchUnit to the hardware schedulers. |
| 244 | class Instruction { |
| 245 | const InstrDesc &Desc; |
| 246 | |
| 247 | enum InstrStage { |
| 248 | IS_INVALID, // Instruction in an invalid state. |
| 249 | IS_AVAILABLE, // Instruction dispatched but operands are not ready. |
| 250 | IS_READY, // Instruction dispatched and operands ready. |
| 251 | IS_EXECUTING, // Instruction issued. |
| 252 | IS_EXECUTED, // Instruction executed. Values are written back. |
| 253 | IS_RETIRED // Instruction retired. |
| 254 | }; |
| 255 | |
| 256 | // The current instruction stage. |
| 257 | enum InstrStage Stage; |
| 258 | |
| 259 | // This value defaults to the instruction latency. This instruction is |
| 260 | // considered executed when field CyclesLeft goes to zero. |
| 261 | int CyclesLeft; |
| 262 | |
| 263 | // Retire Unit token ID for this instruction. |
| 264 | unsigned RCUTokenID; |
| 265 | |
| 266 | using UniqueDef = std::unique_ptr<WriteState>; |
| 267 | using UniqueUse = std::unique_ptr<ReadState>; |
| 268 | using VecDefs = std::vector<UniqueDef>; |
| 269 | using VecUses = std::vector<UniqueUse>; |
| 270 | |
| 271 | // Output dependencies. |
| 272 | // One entry per each implicit and explicit register definition. |
| 273 | VecDefs Defs; |
| 274 | |
| 275 | // Input dependencies. |
| 276 | // One entry per each implicit and explicit register use. |
| 277 | VecUses Uses; |
| 278 | |
| 279 | // This instruction has already been dispatched, and all operands are ready. |
| 280 | void setReady() { |
| 281 | assert(Stage == IS_AVAILABLE); |
| 282 | Stage = IS_READY; |
| 283 | } |
| 284 | |
| 285 | public: |
| 286 | Instruction(const InstrDesc &D) |
| 287 | : Desc(D), Stage(IS_INVALID), CyclesLeft(-1) {} |
| 288 | Instruction(const Instruction &Other) = delete; |
| 289 | Instruction &operator=(const Instruction &Other) = delete; |
| 290 | |
| 291 | VecDefs &getDefs() { return Defs; } |
| 292 | const VecDefs &getDefs() const { return Defs; } |
| 293 | VecUses &getUses() { return Uses; } |
| 294 | const VecUses &getUses() const { return Uses; } |
| 295 | const InstrDesc &getDesc() const { return Desc; } |
| 296 | |
| 297 | unsigned getRCUTokenID() const { return RCUTokenID; } |
| 298 | int getCyclesLeft() const { return CyclesLeft; } |
| 299 | void setCyclesLeft(int Cycles) { CyclesLeft = Cycles; } |
| 300 | void setRCUTokenID(unsigned TokenID) { RCUTokenID = TokenID; } |
| 301 | |
| 302 | // Transition to the dispatch stage. |
| 303 | // No definition is updated because the instruction is not "executing". |
| 304 | void dispatch() { |
| 305 | assert(Stage == IS_INVALID); |
| 306 | Stage = IS_AVAILABLE; |
| 307 | } |
| 308 | |
| 309 | // Instruction issued. Transition to the IS_EXECUTING state, and update |
| 310 | // all the definitions. |
| 311 | void execute(); |
| 312 | |
| 313 | void forceExecuted() { |
| 314 | assert((Stage == IS_INVALID && isZeroLatency()) || |
| 315 | (Stage == IS_READY && Desc.MaxLatency == 0)); |
| 316 | Stage = IS_EXECUTED; |
| 317 | } |
| 318 | |
| 319 | // Checks if operands are available. If all operands area ready, |
| 320 | // then this forces a transition from IS_AVAILABLE to IS_READY. |
| 321 | bool isReady(); |
| 322 | |
| 323 | bool isDispatched() const { return Stage == IS_AVAILABLE; } |
| 324 | bool isExecuting() const { return Stage == IS_EXECUTING; } |
| 325 | bool isExecuted() const { return Stage == IS_EXECUTED; } |
| 326 | bool isZeroLatency() const; |
| 327 | |
| 328 | void retire() { |
| 329 | assert(Stage == IS_EXECUTED); |
| 330 | Stage = IS_RETIRED; |
| 331 | } |
| 332 | |
| 333 | void cycleEvent(); |
| 334 | }; |
| 335 | |
| 336 | } // namespace mca |
| 337 | |
| 338 | #endif |