Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | //===--------------------- RegisterFile.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 a register mapping file class. This class is responsible |
||
| 11 | /// for managing hardware register files and the tracking of data dependencies |
||
| 12 | /// between registers. |
||
| 13 | /// |
||
| 14 | //===----------------------------------------------------------------------===// |
||
| 15 | |||
| 16 | #ifndef LLVM_MCA_HARDWAREUNITS_REGISTERFILE_H |
||
| 17 | #define LLVM_MCA_HARDWAREUNITS_REGISTERFILE_H |
||
| 18 | |||
| 19 | #include "llvm/ADT/APInt.h" |
||
| 20 | #include "llvm/ADT/SmallVector.h" |
||
| 21 | #include "llvm/MC/MCRegisterInfo.h" |
||
| 22 | #include "llvm/MC/MCSchedule.h" |
||
| 23 | #include "llvm/MC/MCSubtargetInfo.h" |
||
| 24 | #include "llvm/MCA/HardwareUnits/HardwareUnit.h" |
||
| 25 | |||
| 26 | namespace llvm { |
||
| 27 | namespace mca { |
||
| 28 | |||
| 29 | class ReadState; |
||
| 30 | class WriteState; |
||
| 31 | class Instruction; |
||
| 32 | |||
| 33 | /// A reference to a register write. |
||
| 34 | /// |
||
| 35 | /// This class is mainly used by the register file to describe register |
||
| 36 | /// mappings. It correlates a register write to the source index of the |
||
| 37 | /// defining instruction. |
||
| 38 | class WriteRef { |
||
| 39 | unsigned IID; |
||
| 40 | unsigned WriteBackCycle; |
||
| 41 | unsigned WriteResID; |
||
| 42 | MCPhysReg RegisterID; |
||
| 43 | WriteState *Write; |
||
| 44 | |||
| 45 | static const unsigned INVALID_IID; |
||
| 46 | |||
| 47 | public: |
||
| 48 | WriteRef() |
||
| 49 | : IID(INVALID_IID), WriteBackCycle(), WriteResID(), RegisterID(), |
||
| 50 | Write() {} |
||
| 51 | WriteRef(unsigned SourceIndex, WriteState *WS); |
||
| 52 | |||
| 53 | unsigned getSourceIndex() const { return IID; } |
||
| 54 | unsigned getWriteBackCycle() const; |
||
| 55 | |||
| 56 | const WriteState *getWriteState() const { return Write; } |
||
| 57 | WriteState *getWriteState() { return Write; } |
||
| 58 | unsigned getWriteResourceID() const; |
||
| 59 | MCPhysReg getRegisterID() const; |
||
| 60 | |||
| 61 | void commit(); |
||
| 62 | void notifyExecuted(unsigned Cycle); |
||
| 63 | |||
| 64 | bool hasKnownWriteBackCycle() const; |
||
| 65 | bool isWriteZero() const; |
||
| 66 | bool isValid() const { return getSourceIndex() != INVALID_IID; } |
||
| 67 | |||
| 68 | /// Returns true if this register write has been executed, and the new |
||
| 69 | /// register value is therefore available to users. |
||
| 70 | bool isAvailable() const { return hasKnownWriteBackCycle(); } |
||
| 71 | |||
| 72 | bool operator==(const WriteRef &Other) const { |
||
| 73 | return Write && Other.Write && Write == Other.Write; |
||
| 74 | } |
||
| 75 | |||
| 76 | #ifndef NDEBUG |
||
| 77 | void dump() const; |
||
| 78 | #endif |
||
| 79 | }; |
||
| 80 | |||
| 81 | /// Manages hardware register files, and tracks register definitions for |
||
| 82 | /// register renaming purposes. |
||
| 83 | class RegisterFile : public HardwareUnit { |
||
| 84 | const MCRegisterInfo &MRI; |
||
| 85 | |||
| 86 | // class RegisterMappingTracker is a physical register file (PRF) descriptor. |
||
| 87 | // There is one RegisterMappingTracker for every PRF definition in the |
||
| 88 | // scheduling model. |
||
| 89 | // |
||
| 90 | // An instance of RegisterMappingTracker tracks the number of physical |
||
| 91 | // registers available for renaming. It also tracks the number of register |
||
| 92 | // moves eliminated per cycle. |
||
| 93 | struct RegisterMappingTracker { |
||
| 94 | // The total number of physical registers that are available in this |
||
| 95 | // register file for register renaming purpouses. A value of zero for this |
||
| 96 | // field means: this register file has an unbounded number of physical |
||
| 97 | // registers. |
||
| 98 | const unsigned NumPhysRegs; |
||
| 99 | // Number of physical registers that are currently in use. |
||
| 100 | unsigned NumUsedPhysRegs; |
||
| 101 | |||
| 102 | // Maximum number of register moves that can be eliminated by this PRF every |
||
| 103 | // cycle. A value of zero means that there is no limit in the number of |
||
| 104 | // moves which can be eliminated every cycle. |
||
| 105 | const unsigned MaxMoveEliminatedPerCycle; |
||
| 106 | |||
| 107 | // Number of register moves eliminated during this cycle. |
||
| 108 | // |
||
| 109 | // This value is increased by one every time a register move is eliminated. |
||
| 110 | // Every new cycle, this value is reset to zero. |
||
| 111 | // A move can be eliminated only if MaxMoveEliminatedPerCycle is zero, or if |
||
| 112 | // NumMoveEliminated is less than MaxMoveEliminatedPerCycle. |
||
| 113 | unsigned NumMoveEliminated; |
||
| 114 | |||
| 115 | // If set, move elimination is restricted to zero-register moves only. |
||
| 116 | bool AllowZeroMoveEliminationOnly; |
||
| 117 | |||
| 118 | RegisterMappingTracker(unsigned NumPhysRegisters, |
||
| 119 | unsigned MaxMoveEliminated = 0U, |
||
| 120 | bool AllowZeroMoveElimOnly = false) |
||
| 121 | : NumPhysRegs(NumPhysRegisters), NumUsedPhysRegs(0), |
||
| 122 | MaxMoveEliminatedPerCycle(MaxMoveEliminated), NumMoveEliminated(0U), |
||
| 123 | AllowZeroMoveEliminationOnly(AllowZeroMoveElimOnly) {} |
||
| 124 | }; |
||
| 125 | |||
| 126 | // A vector of register file descriptors. This set always contains at least |
||
| 127 | // one entry. Entry at index #0 is reserved. That entry describes a register |
||
| 128 | // file with an unbounded number of physical registers that "sees" all the |
||
| 129 | // hardware registers declared by the target (i.e. all the register |
||
| 130 | // definitions in the target specific `XYZRegisterInfo.td` - where `XYZ` is |
||
| 131 | // the target name). |
||
| 132 | // |
||
| 133 | // Users can limit the number of physical registers that are available in |
||
| 134 | // register file #0 specifying command line flag `-register-file-size=<uint>`. |
||
| 135 | SmallVector<RegisterMappingTracker, 4> RegisterFiles; |
||
| 136 | |||
| 137 | // This type is used to propagate information about the owner of a register, |
||
| 138 | // and the cost of allocating it in the PRF. Register cost is defined as the |
||
| 139 | // number of physical registers consumed by the PRF to allocate a user |
||
| 140 | // register. |
||
| 141 | // |
||
| 142 | // For example: on X86 BtVer2, a YMM register consumes 2 128-bit physical |
||
| 143 | // registers. So, the cost of allocating a YMM register in BtVer2 is 2. |
||
| 144 | using IndexPlusCostPairTy = std::pair<unsigned, unsigned>; |
||
| 145 | |||
| 146 | // Struct RegisterRenamingInfo is used to map logical registers to register |
||
| 147 | // files. |
||
| 148 | // |
||
| 149 | // There is a RegisterRenamingInfo object for every logical register defined |
||
| 150 | // by the target. RegisteRenamingInfo objects are stored into vector |
||
| 151 | // `RegisterMappings`, and MCPhysReg IDs can be used to reference |
||
| 152 | // elements in that vector. |
||
| 153 | // |
||
| 154 | // Each RegisterRenamingInfo is owned by a PRF, and field `IndexPlusCost` |
||
| 155 | // specifies both the owning PRF, as well as the number of physical registers |
||
| 156 | // consumed at register renaming stage. |
||
| 157 | // |
||
| 158 | // Field `AllowMoveElimination` is set for registers that are used as |
||
| 159 | // destination by optimizable register moves. |
||
| 160 | // |
||
| 161 | // Field `AliasRegID` is set by writes from register moves that have been |
||
| 162 | // eliminated at register renaming stage. A move eliminated at register |
||
| 163 | // renaming stage is effectively bypassed, and its write aliases the source |
||
| 164 | // register definition. |
||
| 165 | struct RegisterRenamingInfo { |
||
| 166 | IndexPlusCostPairTy IndexPlusCost; |
||
| 167 | MCPhysReg RenameAs; |
||
| 168 | MCPhysReg AliasRegID; |
||
| 169 | bool AllowMoveElimination; |
||
| 170 | RegisterRenamingInfo() |
||
| 171 | : IndexPlusCost(std::make_pair(0U, 1U)), RenameAs(0U), AliasRegID(0U), |
||
| 172 | AllowMoveElimination(false) {} |
||
| 173 | }; |
||
| 174 | |||
| 175 | // RegisterMapping objects are mainly used to track physical register |
||
| 176 | // definitions and resolve data dependencies. |
||
| 177 | // |
||
| 178 | // Every register declared by the Target is associated with an instance of |
||
| 179 | // RegisterMapping. RegisterMapping objects keep track of writes to a logical |
||
| 180 | // register. That information is used by class RegisterFile to resolve data |
||
| 181 | // dependencies, and correctly set latencies for register uses. |
||
| 182 | // |
||
| 183 | // This implementation does not allow overlapping register files. The only |
||
| 184 | // register file that is allowed to overlap with other register files is |
||
| 185 | // register file #0. If we exclude register #0, every register is "owned" by |
||
| 186 | // at most one register file. |
||
| 187 | using RegisterMapping = std::pair<WriteRef, RegisterRenamingInfo>; |
||
| 188 | |||
| 189 | // There is one entry per each register defined by the target. |
||
| 190 | std::vector<RegisterMapping> RegisterMappings; |
||
| 191 | |||
| 192 | // Used to track zero registers. There is one bit for each register defined by |
||
| 193 | // the target. Bits are set for registers that are known to be zero. |
||
| 194 | APInt ZeroRegisters; |
||
| 195 | |||
| 196 | unsigned CurrentCycle; |
||
| 197 | |||
| 198 | // This method creates a new register file descriptor. |
||
| 199 | // The new register file owns all of the registers declared by register |
||
| 200 | // classes in the 'RegisterClasses' set. |
||
| 201 | // |
||
| 202 | // Processor models allow the definition of RegisterFile(s) via tablegen. For |
||
| 203 | // example, this is a tablegen definition for a x86 register file for |
||
| 204 | // XMM[0-15] and YMM[0-15], that allows up to 60 renames (each rename costs 1 |
||
| 205 | // physical register). |
||
| 206 | // |
||
| 207 | // def FPRegisterFile : RegisterFile<60, [VR128RegClass, VR256RegClass]> |
||
| 208 | // |
||
| 209 | // Here FPRegisterFile contains all the registers defined by register class |
||
| 210 | // VR128RegClass and VR256RegClass. FPRegisterFile implements 60 |
||
| 211 | // registers which can be used for register renaming purpose. |
||
| 212 | void addRegisterFile(const MCRegisterFileDesc &RF, |
||
| 213 | ArrayRef<MCRegisterCostEntry> Entries); |
||
| 214 | |||
| 215 | // Consumes physical registers in each register file specified by the |
||
| 216 | // `IndexPlusCostPairTy`. This method is called from `addRegisterMapping()`. |
||
| 217 | void allocatePhysRegs(const RegisterRenamingInfo &Entry, |
||
| 218 | MutableArrayRef<unsigned> UsedPhysRegs); |
||
| 219 | |||
| 220 | // Releases previously allocated physical registers from the register file(s). |
||
| 221 | // This method is called from `invalidateRegisterMapping()`. |
||
| 222 | void freePhysRegs(const RegisterRenamingInfo &Entry, |
||
| 223 | MutableArrayRef<unsigned> FreedPhysRegs); |
||
| 224 | |||
| 225 | // Create an instance of RegisterMappingTracker for every register file |
||
| 226 | // specified by the processor model. |
||
| 227 | // If no register file is specified, then this method creates a default |
||
| 228 | // register file with an unbounded number of physical registers. |
||
| 229 | void initialize(const MCSchedModel &SM, unsigned NumRegs); |
||
| 230 | |||
| 231 | public: |
||
| 232 | RegisterFile(const MCSchedModel &SM, const MCRegisterInfo &mri, |
||
| 233 | unsigned NumRegs = 0); |
||
| 234 | |||
| 235 | // Collects writes that are in a RAW dependency with RS. |
||
| 236 | void collectWrites(const MCSubtargetInfo &STI, const ReadState &RS, |
||
| 237 | SmallVectorImpl<WriteRef> &Writes, |
||
| 238 | SmallVectorImpl<WriteRef> &CommittedWrites) const; |
||
| 239 | struct RAWHazard { |
||
| 240 | MCPhysReg RegisterID; |
||
| 241 | int CyclesLeft; |
||
| 242 | |||
| 243 | RAWHazard() : RegisterID(), CyclesLeft() {} |
||
| 244 | bool isValid() const { return RegisterID; } |
||
| 245 | bool hasUnknownCycles() const { return CyclesLeft < 0; } |
||
| 246 | }; |
||
| 247 | |||
| 248 | RAWHazard checkRAWHazards(const MCSubtargetInfo &STI, |
||
| 249 | const ReadState &RS) const; |
||
| 250 | |||
| 251 | // This method updates the register mappings inserting a new register |
||
| 252 | // definition. This method is also responsible for updating the number of |
||
| 253 | // allocated physical registers in each register file modified by the write. |
||
| 254 | // No physical regiser is allocated if this write is from a zero-idiom. |
||
| 255 | void addRegisterWrite(WriteRef Write, MutableArrayRef<unsigned> UsedPhysRegs); |
||
| 256 | |||
| 257 | // Collect writes that are in a data dependency with RS, and update RS |
||
| 258 | // internal state. |
||
| 259 | void addRegisterRead(ReadState &RS, const MCSubtargetInfo &STI) const; |
||
| 260 | |||
| 261 | // Removes write \param WS from the register mappings. |
||
| 262 | // Physical registers may be released to reflect this update. |
||
| 263 | // No registers are released if this write is from a zero-idiom. |
||
| 264 | void removeRegisterWrite(const WriteState &WS, |
||
| 265 | MutableArrayRef<unsigned> FreedPhysRegs); |
||
| 266 | |||
| 267 | // Returns true if the PRF at index `PRFIndex` can eliminate a move from RS to |
||
| 268 | // WS. |
||
| 269 | bool canEliminateMove(const WriteState &WS, const ReadState &RS, |
||
| 270 | unsigned PRFIndex) const; |
||
| 271 | |||
| 272 | // Returns true if this instruction can be fully eliminated at register |
||
| 273 | // renaming stage. On success, this method updates the internal state of each |
||
| 274 | // WriteState by setting flag `WS.isEliminated`, and by propagating the zero |
||
| 275 | // flag for known zero registers. It internally uses `canEliminateMove` to |
||
| 276 | // determine if a read/write pair can be eliminated. By default, it assumes a |
||
| 277 | // register swap if there is more than one register definition. |
||
| 278 | bool tryEliminateMoveOrSwap(MutableArrayRef<WriteState> Writes, |
||
| 279 | MutableArrayRef<ReadState> Reads); |
||
| 280 | |||
| 281 | // Checks if there are enough physical registers in the register files. |
||
| 282 | // Returns a "response mask" where each bit represents the response from a |
||
| 283 | // different register file. A mask of all zeroes means that all register |
||
| 284 | // files are available. Otherwise, the mask can be used to identify which |
||
| 285 | // register file was busy. This sematic allows us to classify dispatch |
||
| 286 | // stalls caused by the lack of register file resources. |
||
| 287 | // |
||
| 288 | // Current implementation can simulate up to 32 register files (including the |
||
| 289 | // special register file at index #0). |
||
| 290 | unsigned isAvailable(ArrayRef<MCPhysReg> Regs) const; |
||
| 291 | |||
| 292 | // Returns the number of PRFs implemented by this processor. |
||
| 293 | unsigned getNumRegisterFiles() const { return RegisterFiles.size(); } |
||
| 294 | |||
| 295 | unsigned getElapsedCyclesFromWriteBack(const WriteRef &WR) const; |
||
| 296 | |||
| 297 | void onInstructionExecuted(Instruction *IS); |
||
| 298 | |||
| 299 | // Notify each PRF that a new cycle just started. |
||
| 300 | void cycleStart(); |
||
| 301 | |||
| 302 | void cycleEnd() { ++CurrentCycle; } |
||
| 303 | |||
| 304 | #ifndef NDEBUG |
||
| 305 | void dump() const; |
||
| 306 | #endif |
||
| 307 | }; |
||
| 308 | |||
| 309 | } // namespace mca |
||
| 310 | } // namespace llvm |
||
| 311 | |||
| 312 | #endif // LLVM_MCA_HARDWAREUNITS_REGISTERFILE_H |