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 |