File: | lib/MCA/InstrBuilder.cpp |
Warning: | line 559, column 3 Value stored to 'SchedClassID' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--------------------- InstrBuilder.cpp ---------------------*- 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 implements the InstrBuilder interface. |
12 | /// |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "llvm/MCA/InstrBuilder.h" |
16 | #include "llvm/ADT/APInt.h" |
17 | #include "llvm/ADT/DenseMap.h" |
18 | #include "llvm/MC/MCInst.h" |
19 | #include "llvm/Support/Debug.h" |
20 | #include "llvm/Support/WithColor.h" |
21 | #include "llvm/Support/raw_ostream.h" |
22 | |
23 | #define DEBUG_TYPE"llvm-mca" "llvm-mca" |
24 | |
25 | namespace llvm { |
26 | namespace mca { |
27 | |
28 | InstrBuilder::InstrBuilder(const llvm::MCSubtargetInfo &sti, |
29 | const llvm::MCInstrInfo &mcii, |
30 | const llvm::MCRegisterInfo &mri, |
31 | const llvm::MCInstrAnalysis *mcia) |
32 | : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia), FirstCallInst(true), |
33 | FirstReturnInst(true) { |
34 | computeProcResourceMasks(STI.getSchedModel(), ProcResourceMasks); |
35 | } |
36 | |
37 | static void initializeUsedResources(InstrDesc &ID, |
38 | const MCSchedClassDesc &SCDesc, |
39 | const MCSubtargetInfo &STI, |
40 | ArrayRef<uint64_t> ProcResourceMasks) { |
41 | const MCSchedModel &SM = STI.getSchedModel(); |
42 | |
43 | // Populate resources consumed. |
44 | using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>; |
45 | std::vector<ResourcePlusCycles> Worklist; |
46 | |
47 | // Track cycles contributed by resources that are in a "Super" relationship. |
48 | // This is required if we want to correctly match the behavior of method |
49 | // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set |
50 | // of "consumed" processor resources and resource cycles, the logic in |
51 | // ExpandProcResource() doesn't update the number of resource cycles |
52 | // contributed by a "Super" resource to a group. |
53 | // We need to take this into account when we find that a processor resource is |
54 | // part of a group, and it is also used as the "Super" of other resources. |
55 | // This map stores the number of cycles contributed by sub-resources that are |
56 | // part of a "Super" resource. The key value is the "Super" resource mask ID. |
57 | DenseMap<uint64_t, unsigned> SuperResources; |
58 | |
59 | unsigned NumProcResources = SM.getNumProcResourceKinds(); |
60 | APInt Buffers(NumProcResources, 0); |
61 | |
62 | for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) { |
63 | const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I; |
64 | const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx); |
65 | uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx]; |
66 | if (PR.BufferSize != -1) |
67 | Buffers.setBit(PRE->ProcResourceIdx); |
68 | CycleSegment RCy(0, PRE->Cycles, false); |
69 | Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy))); |
70 | if (PR.SuperIdx) { |
71 | uint64_t Super = ProcResourceMasks[PR.SuperIdx]; |
72 | SuperResources[Super] += PRE->Cycles; |
73 | } |
74 | } |
75 | |
76 | // Sort elements by mask popcount, so that we prioritize resource units over |
77 | // resource groups, and smaller groups over larger groups. |
78 | sort(Worklist, [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) { |
79 | unsigned popcntA = countPopulation(A.first); |
80 | unsigned popcntB = countPopulation(B.first); |
81 | if (popcntA < popcntB) |
82 | return true; |
83 | if (popcntA > popcntB) |
84 | return false; |
85 | return A.first < B.first; |
86 | }); |
87 | |
88 | uint64_t UsedResourceUnits = 0; |
89 | |
90 | // Remove cycles contributed by smaller resources. |
91 | for (unsigned I = 0, E = Worklist.size(); I < E; ++I) { |
92 | ResourcePlusCycles &A = Worklist[I]; |
93 | if (!A.second.size()) { |
94 | A.second.NumUnits = 0; |
95 | A.second.setReserved(); |
96 | ID.Resources.emplace_back(A); |
97 | continue; |
98 | } |
99 | |
100 | ID.Resources.emplace_back(A); |
101 | uint64_t NormalizedMask = A.first; |
102 | if (countPopulation(A.first) == 1) { |
103 | UsedResourceUnits |= A.first; |
104 | } else { |
105 | // Remove the leading 1 from the resource group mask. |
106 | NormalizedMask ^= PowerOf2Floor(NormalizedMask); |
107 | } |
108 | |
109 | for (unsigned J = I + 1; J < E; ++J) { |
110 | ResourcePlusCycles &B = Worklist[J]; |
111 | if ((NormalizedMask & B.first) == NormalizedMask) { |
112 | B.second.CS.subtract(A.second.size() - SuperResources[A.first]); |
113 | if (countPopulation(B.first) > 1) |
114 | B.second.NumUnits++; |
115 | } |
116 | } |
117 | } |
118 | |
119 | // A SchedWrite may specify a number of cycles in which a resource group |
120 | // is reserved. For example (on target x86; cpu Haswell): |
121 | // |
122 | // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> { |
123 | // let ResourceCycles = [2, 2, 3]; |
124 | // } |
125 | // |
126 | // This means: |
127 | // Resource units HWPort0 and HWPort1 are both used for 2cy. |
128 | // Resource group HWPort01 is the union of HWPort0 and HWPort1. |
129 | // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01 |
130 | // will not be usable for 2 entire cycles from instruction issue. |
131 | // |
132 | // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency |
133 | // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an |
134 | // extra delay on top of the 2 cycles latency. |
135 | // During those extra cycles, HWPort01 is not usable by other instructions. |
136 | for (ResourcePlusCycles &RPC : ID.Resources) { |
137 | if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) { |
138 | // Remove the leading 1 from the resource group mask. |
139 | uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first); |
140 | if ((Mask & UsedResourceUnits) == Mask) |
141 | RPC.second.setReserved(); |
142 | } |
143 | } |
144 | |
145 | // Identify extra buffers that are consumed through super resources. |
146 | for (const std::pair<uint64_t, unsigned> &SR : SuperResources) { |
147 | for (unsigned I = 1, E = NumProcResources; I < E; ++I) { |
148 | const MCProcResourceDesc &PR = *SM.getProcResource(I); |
149 | if (PR.BufferSize == -1) |
150 | continue; |
151 | |
152 | uint64_t Mask = ProcResourceMasks[I]; |
153 | if (Mask != SR.first && ((Mask & SR.first) == SR.first)) |
154 | Buffers.setBit(I); |
155 | } |
156 | } |
157 | |
158 | // Now set the buffers. |
159 | if (unsigned NumBuffers = Buffers.countPopulation()) { |
160 | ID.Buffers.resize(NumBuffers); |
161 | for (unsigned I = 0, E = NumProcResources; I < E && NumBuffers; ++I) { |
162 | if (Buffers[I]) { |
163 | --NumBuffers; |
164 | ID.Buffers[NumBuffers] = ProcResourceMasks[I]; |
165 | } |
166 | } |
167 | } |
168 | |
169 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false) |
170 | for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false) |
171 | dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false) |
172 | for (const uint64_t R : ID.Buffers)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false) |
173 | dbgs() << "\t\tBuffer Mask=" << R << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false) |
174 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { for (const std::pair<uint64_t, ResourceUsage > &R : ID.Resources) dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n' ; for (const uint64_t R : ID.Buffers) dbgs() << "\t\tBuffer Mask=" << R << '\n'; }; } } while (false); |
175 | } |
176 | |
177 | static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc, |
178 | const MCSchedClassDesc &SCDesc, |
179 | const MCSubtargetInfo &STI) { |
180 | if (MCDesc.isCall()) { |
181 | // We cannot estimate how long this call will take. |
182 | // Artificially set an arbitrarily high latency (100cy). |
183 | ID.MaxLatency = 100U; |
184 | return; |
185 | } |
186 | |
187 | int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
188 | // If latency is unknown, then conservatively assume a MaxLatency of 100cy. |
189 | ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency); |
190 | } |
191 | |
192 | static Error verifyOperands(const MCInstrDesc &MCDesc, const MCInst &MCI) { |
193 | // Count register definitions, and skip non register operands in the process. |
194 | unsigned I, E; |
195 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
196 | for (I = 0, E = MCI.getNumOperands(); NumExplicitDefs && I < E; ++I) { |
197 | const MCOperand &Op = MCI.getOperand(I); |
198 | if (Op.isReg()) |
199 | --NumExplicitDefs; |
200 | } |
201 | |
202 | if (NumExplicitDefs) { |
203 | return make_error<InstructionError<MCInst>>( |
204 | "Expected more register operand definitions.", MCI); |
205 | } |
206 | |
207 | if (MCDesc.hasOptionalDef()) { |
208 | // Always assume that the optional definition is the last operand. |
209 | const MCOperand &Op = MCI.getOperand(MCDesc.getNumOperands() - 1); |
210 | if (I == MCI.getNumOperands() || !Op.isReg()) { |
211 | std::string Message = |
212 | "expected a register operand for an optional definition. Instruction " |
213 | "has not been correctly analyzed."; |
214 | return make_error<InstructionError<MCInst>>(Message, MCI); |
215 | } |
216 | } |
217 | |
218 | return ErrorSuccess(); |
219 | } |
220 | |
221 | void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI, |
222 | unsigned SchedClassID) { |
223 | const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode()); |
224 | const MCSchedModel &SM = STI.getSchedModel(); |
225 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
226 | |
227 | // Assumptions made by this algorithm: |
228 | // 1. The number of explicit and implicit register definitions in a MCInst |
229 | // matches the number of explicit and implicit definitions according to |
230 | // the opcode descriptor (MCInstrDesc). |
231 | // 2. Uses start at index #(MCDesc.getNumDefs()). |
232 | // 3. There can only be a single optional register definition, an it is |
233 | // always the last operand of the sequence (excluding extra operands |
234 | // contributed by variadic opcodes). |
235 | // |
236 | // These assumptions work quite well for most out-of-order in-tree targets |
237 | // like x86. This is mainly because the vast majority of instructions is |
238 | // expanded to MCInst using a straightforward lowering logic that preserves |
239 | // the ordering of the operands. |
240 | // |
241 | // About assumption 1. |
242 | // The algorithm allows non-register operands between register operand |
243 | // definitions. This helps to handle some special ARM instructions with |
244 | // implicit operand increment (-mtriple=armv7): |
245 | // |
246 | // vld1.32 {d18, d19}, [r1]! @ <MCInst #1463 VLD1q32wb_fixed |
247 | // @ <MCOperand Reg:59> |
248 | // @ <MCOperand Imm:0> (!!) |
249 | // @ <MCOperand Reg:67> |
250 | // @ <MCOperand Imm:0> |
251 | // @ <MCOperand Imm:14> |
252 | // @ <MCOperand Reg:0>> |
253 | // |
254 | // MCDesc reports: |
255 | // 6 explicit operands. |
256 | // 1 optional definition |
257 | // 2 explicit definitions (!!) |
258 | // |
259 | // The presence of an 'Imm' operand between the two register definitions |
260 | // breaks the assumption that "register definitions are always at the |
261 | // beginning of the operand sequence". |
262 | // |
263 | // To workaround this issue, this algorithm ignores (i.e. skips) any |
264 | // non-register operands between register definitions. The optional |
265 | // definition is still at index #(NumOperands-1). |
266 | // |
267 | // According to assumption 2. register reads start at #(NumExplicitDefs-1). |
268 | // That means, register R1 from the example is both read and written. |
269 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
270 | unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs(); |
271 | unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries; |
272 | unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs; |
273 | if (MCDesc.hasOptionalDef()) |
274 | TotalDefs++; |
275 | |
276 | unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands(); |
277 | ID.Writes.resize(TotalDefs + NumVariadicOps); |
278 | // Iterate over the operands list, and skip non-register operands. |
279 | // The first NumExplictDefs register operands are expected to be register |
280 | // definitions. |
281 | unsigned CurrentDef = 0; |
282 | unsigned i = 0; |
283 | for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) { |
284 | const MCOperand &Op = MCI.getOperand(i); |
285 | if (!Op.isReg()) |
286 | continue; |
287 | |
288 | WriteDescriptor &Write = ID.Writes[CurrentDef]; |
289 | Write.OpIndex = i; |
290 | if (CurrentDef < NumWriteLatencyEntries) { |
291 | const MCWriteLatencyEntry &WLE = |
292 | *STI.getWriteLatencyEntry(&SCDesc, CurrentDef); |
293 | // Conservatively default to MaxLatency. |
294 | Write.Latency = |
295 | WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles); |
296 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
297 | } else { |
298 | // Assign a default latency for this write. |
299 | Write.Latency = ID.MaxLatency; |
300 | Write.SClassOrWriteResourceID = 0; |
301 | } |
302 | Write.IsOptionalDef = false; |
303 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
304 | dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
305 | << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
306 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
307 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
308 | CurrentDef++; |
309 | } |
310 | |
311 | assert(CurrentDef == NumExplicitDefs &&((CurrentDef == NumExplicitDefs && "Expected more register operand definitions." ) ? static_cast<void> (0) : __assert_fail ("CurrentDef == NumExplicitDefs && \"Expected more register operand definitions.\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 312, __PRETTY_FUNCTION__)) |
312 | "Expected more register operand definitions.")((CurrentDef == NumExplicitDefs && "Expected more register operand definitions." ) ? static_cast<void> (0) : __assert_fail ("CurrentDef == NumExplicitDefs && \"Expected more register operand definitions.\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 312, __PRETTY_FUNCTION__)); |
313 | for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) { |
314 | unsigned Index = NumExplicitDefs + CurrentDef; |
315 | WriteDescriptor &Write = ID.Writes[Index]; |
316 | Write.OpIndex = ~CurrentDef; |
317 | Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef]; |
318 | if (Index < NumWriteLatencyEntries) { |
319 | const MCWriteLatencyEntry &WLE = |
320 | *STI.getWriteLatencyEntry(&SCDesc, Index); |
321 | // Conservatively default to MaxLatency. |
322 | Write.Latency = |
323 | WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles); |
324 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
325 | } else { |
326 | // Assign a default latency for this write. |
327 | Write.Latency = ID.MaxLatency; |
328 | Write.SClassOrWriteResourceID = 0; |
329 | } |
330 | |
331 | Write.IsOptionalDef = false; |
332 | assert(Write.RegisterID != 0 && "Expected a valid phys register!")((Write.RegisterID != 0 && "Expected a valid phys register!" ) ? static_cast<void> (0) : __assert_fail ("Write.RegisterID != 0 && \"Expected a valid phys register!\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 332, __PRETTY_FUNCTION__)); |
333 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
334 | dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
335 | << ", PhysReg=" << MRI.getName(Write.RegisterID)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
336 | << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
337 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
338 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
339 | } |
340 | |
341 | if (MCDesc.hasOptionalDef()) { |
342 | WriteDescriptor &Write = ID.Writes[NumExplicitDefs + NumImplicitDefs]; |
343 | Write.OpIndex = MCDesc.getNumOperands() - 1; |
344 | // Assign a default latency for this write. |
345 | Write.Latency = ID.MaxLatency; |
346 | Write.SClassOrWriteResourceID = 0; |
347 | Write.IsOptionalDef = true; |
348 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
349 | dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
350 | << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
351 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
352 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
353 | } |
354 | |
355 | if (!NumVariadicOps) |
356 | return; |
357 | |
358 | // FIXME: if an instruction opcode is flagged 'mayStore', and it has no |
359 | // "unmodeledSideEffects', then this logic optimistically assumes that any |
360 | // extra register operands in the variadic sequence is not a register |
361 | // definition. |
362 | // |
363 | // Otherwise, we conservatively assume that any register operand from the |
364 | // variadic sequence is both a register read and a register write. |
365 | bool AssumeUsesOnly = MCDesc.mayStore() && !MCDesc.mayLoad() && |
366 | !MCDesc.hasUnmodeledSideEffects(); |
367 | CurrentDef = NumExplicitDefs + NumImplicitDefs + MCDesc.hasOptionalDef(); |
368 | for (unsigned I = 0, OpIndex = MCDesc.getNumOperands(); |
369 | I < NumVariadicOps && !AssumeUsesOnly; ++I, ++OpIndex) { |
370 | const MCOperand &Op = MCI.getOperand(OpIndex); |
371 | if (!Op.isReg()) |
372 | continue; |
373 | |
374 | WriteDescriptor &Write = ID.Writes[CurrentDef]; |
375 | Write.OpIndex = OpIndex; |
376 | // Assign a default latency for this write. |
377 | Write.Latency = ID.MaxLatency; |
378 | Write.SClassOrWriteResourceID = 0; |
379 | Write.IsOptionalDef = false; |
380 | ++CurrentDef; |
381 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
382 | dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
383 | << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
384 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
385 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
386 | } |
387 | |
388 | ID.Writes.resize(CurrentDef); |
389 | } |
390 | |
391 | void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI, |
392 | unsigned SchedClassID) { |
393 | const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode()); |
394 | unsigned NumExplicitUses = MCDesc.getNumOperands() - MCDesc.getNumDefs(); |
395 | unsigned NumImplicitUses = MCDesc.getNumImplicitUses(); |
396 | // Remove the optional definition. |
397 | if (MCDesc.hasOptionalDef()) |
398 | --NumExplicitUses; |
399 | unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands(); |
400 | unsigned TotalUses = NumExplicitUses + NumImplicitUses + NumVariadicOps; |
401 | ID.Reads.resize(TotalUses); |
402 | unsigned CurrentUse = 0; |
403 | for (unsigned I = 0, OpIndex = MCDesc.getNumDefs(); I < NumExplicitUses; |
404 | ++I, ++OpIndex) { |
405 | const MCOperand &Op = MCI.getOperand(OpIndex); |
406 | if (!Op.isReg()) |
407 | continue; |
408 | |
409 | ReadDescriptor &Read = ID.Reads[CurrentUse]; |
410 | Read.OpIndex = OpIndex; |
411 | Read.UseIndex = I; |
412 | Read.SchedClassID = SchedClassID; |
413 | ++CurrentUse; |
414 | LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", UseIndex=" << Read.UseIndex << '\n'; } } while (false) |
415 | << ", UseIndex=" << Read.UseIndex << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", UseIndex=" << Read.UseIndex << '\n'; } } while (false); |
416 | } |
417 | |
418 | // For the purpose of ReadAdvance, implicit uses come directly after explicit |
419 | // uses. The "UseIndex" must be updated according to that implicit layout. |
420 | for (unsigned I = 0; I < NumImplicitUses; ++I) { |
421 | ReadDescriptor &Read = ID.Reads[CurrentUse + I]; |
422 | Read.OpIndex = ~I; |
423 | Read.UseIndex = NumExplicitUses + I; |
424 | Read.RegisterID = MCDesc.getImplicitUses()[I]; |
425 | Read.SchedClassID = SchedClassID; |
426 | LLVM_DEBUG(dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndex << ", UseIndex=" << Read.UseIndex << ", RegisterID=" << MRI.getName(Read.RegisterID) << '\n'; } } while (false) |
427 | << ", UseIndex=" << Read.UseIndex << ", RegisterID="do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndex << ", UseIndex=" << Read.UseIndex << ", RegisterID=" << MRI.getName(Read.RegisterID) << '\n'; } } while (false) |
428 | << MRI.getName(Read.RegisterID) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndex << ", UseIndex=" << Read.UseIndex << ", RegisterID=" << MRI.getName(Read.RegisterID) << '\n'; } } while (false); |
429 | } |
430 | |
431 | CurrentUse += NumImplicitUses; |
432 | |
433 | // FIXME: If an instruction opcode is marked as 'mayLoad', and it has no |
434 | // "unmodeledSideEffects", then this logic optimistically assumes that any |
435 | // extra register operands in the variadic sequence are not register |
436 | // definition. |
437 | |
438 | bool AssumeDefsOnly = !MCDesc.mayStore() && MCDesc.mayLoad() && |
439 | !MCDesc.hasUnmodeledSideEffects(); |
440 | for (unsigned I = 0, OpIndex = MCDesc.getNumOperands(); |
441 | I < NumVariadicOps && !AssumeDefsOnly; ++I, ++OpIndex) { |
442 | const MCOperand &Op = MCI.getOperand(OpIndex); |
443 | if (!Op.isReg()) |
444 | continue; |
445 | |
446 | ReadDescriptor &Read = ID.Reads[CurrentUse]; |
447 | Read.OpIndex = OpIndex; |
448 | Read.UseIndex = NumExplicitUses + NumImplicitUses + I; |
449 | Read.SchedClassID = SchedClassID; |
450 | ++CurrentUse; |
451 | LLVM_DEBUG(dbgs() << "\t\t[Use][V] OpIdx=" << Read.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use][V] OpIdx=" << Read.OpIndex << ", UseIndex=" << Read.UseIndex << '\n'; } } while (false) |
452 | << ", UseIndex=" << Read.UseIndex << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use][V] OpIdx=" << Read.OpIndex << ", UseIndex=" << Read.UseIndex << '\n'; } } while (false); |
453 | } |
454 | |
455 | ID.Reads.resize(CurrentUse); |
456 | } |
457 | |
458 | Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID, |
459 | const MCInst &MCI) const { |
460 | if (ID.NumMicroOps != 0) |
461 | return ErrorSuccess(); |
462 | |
463 | bool UsesMemory = ID.MayLoad || ID.MayStore; |
464 | bool UsesBuffers = !ID.Buffers.empty(); |
465 | bool UsesResources = !ID.Resources.empty(); |
466 | if (!UsesMemory && !UsesBuffers && !UsesResources) |
467 | return ErrorSuccess(); |
468 | |
469 | StringRef Message; |
470 | if (UsesMemory) { |
471 | Message = "found an inconsistent instruction that decodes " |
472 | "into zero opcodes and that consumes load/store " |
473 | "unit resources."; |
474 | } else { |
475 | Message = "found an inconsistent instruction that decodes " |
476 | "to zero opcodes and that consumes scheduler " |
477 | "resources."; |
478 | } |
479 | |
480 | return make_error<InstructionError<MCInst>>(Message, MCI); |
481 | } |
482 | |
483 | Expected<const InstrDesc &> |
484 | InstrBuilder::createInstrDescImpl(const MCInst &MCI) { |
485 | assert(STI.getSchedModel().hasInstrSchedModel() &&((STI.getSchedModel().hasInstrSchedModel() && "Itineraries are not yet supported!" ) ? static_cast<void> (0) : __assert_fail ("STI.getSchedModel().hasInstrSchedModel() && \"Itineraries are not yet supported!\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 486, __PRETTY_FUNCTION__)) |
486 | "Itineraries are not yet supported!")((STI.getSchedModel().hasInstrSchedModel() && "Itineraries are not yet supported!" ) ? static_cast<void> (0) : __assert_fail ("STI.getSchedModel().hasInstrSchedModel() && \"Itineraries are not yet supported!\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 486, __PRETTY_FUNCTION__)); |
487 | |
488 | // Obtain the instruction descriptor from the opcode. |
489 | unsigned short Opcode = MCI.getOpcode(); |
490 | const MCInstrDesc &MCDesc = MCII.get(Opcode); |
491 | const MCSchedModel &SM = STI.getSchedModel(); |
492 | |
493 | // Then obtain the scheduling class information from the instruction. |
494 | unsigned SchedClassID = MCDesc.getSchedClass(); |
495 | bool IsVariant = SM.getSchedClassDesc(SchedClassID)->isVariant(); |
496 | |
497 | // Try to solve variant scheduling classes. |
498 | if (IsVariant) { |
499 | unsigned CPUID = SM.getProcessorID(); |
500 | while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant()) |
501 | SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID); |
502 | |
503 | if (!SchedClassID) { |
504 | return make_error<InstructionError<MCInst>>( |
505 | "unable to resolve scheduling class for write variant.", MCI); |
506 | } |
507 | } |
508 | |
509 | // Check if this instruction is supported. Otherwise, report an error. |
510 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
511 | if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) { |
512 | return make_error<InstructionError<MCInst>>( |
513 | "found an unsupported instruction in the input assembly sequence.", |
514 | MCI); |
515 | } |
516 | |
517 | // Create a new empty descriptor. |
518 | std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>(); |
519 | ID->NumMicroOps = SCDesc.NumMicroOps; |
520 | |
521 | if (MCDesc.isCall() && FirstCallInst) { |
522 | // We don't correctly model calls. |
523 | WithColor::warning() << "found a call in the input assembly sequence.\n"; |
524 | WithColor::note() << "call instructions are not correctly modeled. " |
525 | << "Assume a latency of 100cy.\n"; |
526 | FirstCallInst = false; |
527 | } |
528 | |
529 | if (MCDesc.isReturn() && FirstReturnInst) { |
530 | WithColor::warning() << "found a return instruction in the input" |
531 | << " assembly sequence.\n"; |
532 | WithColor::note() << "program counter updates are ignored.\n"; |
533 | FirstReturnInst = false; |
534 | } |
535 | |
536 | ID->MayLoad = MCDesc.mayLoad(); |
537 | ID->MayStore = MCDesc.mayStore(); |
538 | ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects(); |
539 | ID->BeginGroup = SCDesc.BeginGroup; |
540 | ID->EndGroup = SCDesc.EndGroup; |
541 | |
542 | initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks); |
543 | computeMaxLatency(*ID, MCDesc, SCDesc, STI); |
544 | |
545 | if (Error Err = verifyOperands(MCDesc, MCI)) |
546 | return std::move(Err); |
547 | |
548 | populateWrites(*ID, MCI, SchedClassID); |
549 | populateReads(*ID, MCI, SchedClassID); |
550 | |
551 | LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tMaxLatency=" << ID ->MaxLatency << '\n'; } } while (false); |
552 | LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tNumMicroOps=" << ID ->NumMicroOps << '\n'; } } while (false); |
553 | |
554 | // Sanity check on the instruction descriptor. |
555 | if (Error Err = verifyInstrDesc(*ID, MCI)) |
556 | return std::move(Err); |
557 | |
558 | // Now add the new descriptor. |
559 | SchedClassID = MCDesc.getSchedClass(); |
Value stored to 'SchedClassID' is never read | |
560 | bool IsVariadic = MCDesc.isVariadic(); |
561 | if (!IsVariadic && !IsVariant) { |
562 | Descriptors[MCI.getOpcode()] = std::move(ID); |
563 | return *Descriptors[MCI.getOpcode()]; |
564 | } |
565 | |
566 | VariantDescriptors[&MCI] = std::move(ID); |
567 | return *VariantDescriptors[&MCI]; |
568 | } |
569 | |
570 | Expected<const InstrDesc &> |
571 | InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { |
572 | if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end()) |
573 | return *Descriptors[MCI.getOpcode()]; |
574 | |
575 | if (VariantDescriptors.find(&MCI) != VariantDescriptors.end()) |
576 | return *VariantDescriptors[&MCI]; |
577 | |
578 | return createInstrDescImpl(MCI); |
579 | } |
580 | |
581 | Expected<std::unique_ptr<Instruction>> |
582 | InstrBuilder::createInstruction(const MCInst &MCI) { |
583 | Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI); |
584 | if (!DescOrErr) |
585 | return DescOrErr.takeError(); |
586 | const InstrDesc &D = *DescOrErr; |
587 | std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D); |
588 | |
589 | // Check if this is a dependency breaking instruction. |
590 | APInt Mask; |
591 | |
592 | bool IsZeroIdiom = false; |
593 | bool IsDepBreaking = false; |
594 | if (MCIA) { |
595 | unsigned ProcID = STI.getSchedModel().getProcessorID(); |
596 | IsZeroIdiom = MCIA->isZeroIdiom(MCI, Mask, ProcID); |
597 | IsDepBreaking = |
598 | IsZeroIdiom || MCIA->isDependencyBreaking(MCI, Mask, ProcID); |
599 | if (MCIA->isOptimizableRegisterMove(MCI, ProcID)) |
600 | NewIS->setOptimizableMove(); |
601 | } |
602 | |
603 | // Initialize Reads first. |
604 | for (const ReadDescriptor &RD : D.Reads) { |
605 | int RegID = -1; |
606 | if (!RD.isImplicitRead()) { |
607 | // explicit read. |
608 | const MCOperand &Op = MCI.getOperand(RD.OpIndex); |
609 | // Skip non-register operands. |
610 | if (!Op.isReg()) |
611 | continue; |
612 | RegID = Op.getReg(); |
613 | } else { |
614 | // Implicit read. |
615 | RegID = RD.RegisterID; |
616 | } |
617 | |
618 | // Skip invalid register operands. |
619 | if (!RegID) |
620 | continue; |
621 | |
622 | // Okay, this is a register operand. Create a ReadState for it. |
623 | assert(RegID > 0 && "Invalid register ID found!")((RegID > 0 && "Invalid register ID found!") ? static_cast <void> (0) : __assert_fail ("RegID > 0 && \"Invalid register ID found!\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 623, __PRETTY_FUNCTION__)); |
624 | NewIS->getUses().emplace_back(RD, RegID); |
625 | ReadState &RS = NewIS->getUses().back(); |
626 | |
627 | if (IsDepBreaking) { |
628 | // A mask of all zeroes means: explicit input operands are not |
629 | // independent. |
630 | if (Mask.isNullValue()) { |
631 | if (!RD.isImplicitRead()) |
632 | RS.setIndependentFromDef(); |
633 | } else { |
634 | // Check if this register operand is independent according to `Mask`. |
635 | // Note that Mask may not have enough bits to describe all explicit and |
636 | // implicit input operands. If this register operand doesn't have a |
637 | // corresponding bit in Mask, then conservatively assume that it is |
638 | // dependent. |
639 | if (Mask.getBitWidth() > RD.UseIndex) { |
640 | // Okay. This map describe register use `RD.UseIndex`. |
641 | if (Mask[RD.UseIndex]) |
642 | RS.setIndependentFromDef(); |
643 | } |
644 | } |
645 | } |
646 | } |
647 | |
648 | // Early exit if there are no writes. |
649 | if (D.Writes.empty()) |
650 | return std::move(NewIS); |
651 | |
652 | // Track register writes that implicitly clear the upper portion of the |
653 | // underlying super-registers using an APInt. |
654 | APInt WriteMask(D.Writes.size(), 0); |
655 | |
656 | // Now query the MCInstrAnalysis object to obtain information about which |
657 | // register writes implicitly clear the upper portion of a super-register. |
658 | if (MCIA) |
659 | MCIA->clearsSuperRegisters(MRI, MCI, WriteMask); |
660 | |
661 | // Initialize writes. |
662 | unsigned WriteIndex = 0; |
663 | for (const WriteDescriptor &WD : D.Writes) { |
664 | unsigned RegID = WD.isImplicitWrite() ? WD.RegisterID |
665 | : MCI.getOperand(WD.OpIndex).getReg(); |
666 | // Check if this is a optional definition that references NoReg. |
667 | if (WD.IsOptionalDef && !RegID) { |
668 | ++WriteIndex; |
669 | continue; |
670 | } |
671 | |
672 | assert(RegID && "Expected a valid register ID!")((RegID && "Expected a valid register ID!") ? static_cast <void> (0) : __assert_fail ("RegID && \"Expected a valid register ID!\"" , "/build/llvm-toolchain-snapshot-8~svn350071/lib/MCA/InstrBuilder.cpp" , 672, __PRETTY_FUNCTION__)); |
673 | NewIS->getDefs().emplace_back(WD, RegID, |
674 | /* ClearsSuperRegs */ WriteMask[WriteIndex], |
675 | /* WritesZero */ IsZeroIdiom); |
676 | ++WriteIndex; |
677 | } |
678 | |
679 | return std::move(NewIS); |
680 | } |
681 | } // namespace mca |
682 | } // namespace llvm |