File: | tools/llvm-mca/InstrBuilder.cpp |
Warning: | line 274, column 3 Value stored to 'CurrentDef' 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 "InstrBuilder.h" |
16 | #include "llvm/MC/MCInst.h" |
17 | #include "llvm/Support/Debug.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | |
20 | #define DEBUG_TYPE"llvm-mca" "llvm-mca" |
21 | |
22 | namespace mca { |
23 | |
24 | using namespace llvm; |
25 | |
26 | static void initializeUsedResources(InstrDesc &ID, |
27 | const MCSchedClassDesc &SCDesc, |
28 | const MCSubtargetInfo &STI, |
29 | ArrayRef<uint64_t> ProcResourceMasks) { |
30 | const MCSchedModel &SM = STI.getSchedModel(); |
31 | |
32 | // Populate resources consumed. |
33 | using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>; |
34 | std::vector<ResourcePlusCycles> Worklist; |
35 | for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) { |
36 | const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I; |
37 | const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx); |
38 | uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx]; |
39 | if (PR.BufferSize != -1) |
40 | ID.Buffers.push_back(Mask); |
41 | CycleSegment RCy(0, PRE->Cycles, false); |
42 | Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy))); |
43 | } |
44 | |
45 | // Sort elements by mask popcount, so that we prioritize resource units over |
46 | // resource groups, and smaller groups over larger groups. |
47 | llvm::sort(Worklist.begin(), Worklist.end(), |
48 | [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) { |
49 | unsigned popcntA = countPopulation(A.first); |
50 | unsigned popcntB = countPopulation(B.first); |
51 | if (popcntA < popcntB) |
52 | return true; |
53 | if (popcntA > popcntB) |
54 | return false; |
55 | return A.first < B.first; |
56 | }); |
57 | |
58 | uint64_t UsedResourceUnits = 0; |
59 | |
60 | // Remove cycles contributed by smaller resources. |
61 | for (unsigned I = 0, E = Worklist.size(); I < E; ++I) { |
62 | ResourcePlusCycles &A = Worklist[I]; |
63 | if (!A.second.size()) { |
64 | A.second.NumUnits = 0; |
65 | A.second.setReserved(); |
66 | ID.Resources.emplace_back(A); |
67 | continue; |
68 | } |
69 | |
70 | ID.Resources.emplace_back(A); |
71 | uint64_t NormalizedMask = A.first; |
72 | if (countPopulation(A.first) == 1) { |
73 | UsedResourceUnits |= A.first; |
74 | } else { |
75 | // Remove the leading 1 from the resource group mask. |
76 | NormalizedMask ^= PowerOf2Floor(NormalizedMask); |
77 | } |
78 | |
79 | for (unsigned J = I + 1; J < E; ++J) { |
80 | ResourcePlusCycles &B = Worklist[J]; |
81 | if ((NormalizedMask & B.first) == NormalizedMask) { |
82 | B.second.CS.Subtract(A.second.size()); |
83 | if (countPopulation(B.first) > 1) |
84 | B.second.NumUnits++; |
85 | } |
86 | } |
87 | } |
88 | |
89 | // A SchedWrite may specify a number of cycles in which a resource group |
90 | // is reserved. For example (on target x86; cpu Haswell): |
91 | // |
92 | // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> { |
93 | // let ResourceCycles = [2, 2, 3]; |
94 | // } |
95 | // |
96 | // This means: |
97 | // Resource units HWPort0 and HWPort1 are both used for 2cy. |
98 | // Resource group HWPort01 is the union of HWPort0 and HWPort1. |
99 | // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01 |
100 | // will not be usable for 2 entire cycles from instruction issue. |
101 | // |
102 | // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency |
103 | // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an |
104 | // extra delay on top of the 2 cycles latency. |
105 | // During those extra cycles, HWPort01 is not usable by other instructions. |
106 | for (ResourcePlusCycles &RPC : ID.Resources) { |
107 | if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) { |
108 | // Remove the leading 1 from the resource group mask. |
109 | uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first); |
110 | if ((Mask & UsedResourceUnits) == Mask) |
111 | RPC.second.setReserved(); |
112 | } |
113 | } |
114 | |
115 | 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) |
116 | 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) |
117 | 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) |
118 | 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) |
119 | 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) |
120 | })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); |
121 | } |
122 | |
123 | static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc, |
124 | const MCSchedClassDesc &SCDesc, |
125 | const MCSubtargetInfo &STI) { |
126 | if (MCDesc.isCall()) { |
127 | // We cannot estimate how long this call will take. |
128 | // Artificially set an arbitrarily high latency (100cy). |
129 | ID.MaxLatency = 100U; |
130 | return; |
131 | } |
132 | |
133 | int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
134 | // If latency is unknown, then conservatively assume a MaxLatency of 100cy. |
135 | ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency); |
136 | } |
137 | |
138 | static void populateWrites(InstrDesc &ID, const MCInst &MCI, |
139 | const MCInstrDesc &MCDesc, |
140 | const MCSchedClassDesc &SCDesc, |
141 | const MCSubtargetInfo &STI) { |
142 | computeMaxLatency(ID, MCDesc, SCDesc, STI); |
143 | |
144 | // Set if writes through this opcode may update super registers. |
145 | // TODO: on x86-64, a 4 byte write of a general purpose register always |
146 | // fully updates the super-register. |
147 | // More in general, (at least on x86) not all register writes perform |
148 | // a partial (super-)register update. |
149 | // For example, an AVX instruction that writes on a XMM register implicitly |
150 | // zeroes the upper half of every aliasing super-register. |
151 | // |
152 | // For now, we pessimistically assume that writes are all potentially |
153 | // partial register updates. This is a good default for most targets, execept |
154 | // for those like x86 which implement a special semantic for certain opcodes. |
155 | // At least on x86, this may lead to an inaccurate prediction of the |
156 | // instruction level parallelism. |
157 | bool FullyUpdatesSuperRegisters = false; |
158 | |
159 | // Now Populate Writes. |
160 | |
161 | // This algorithm currently works under the strong (and potentially incorrect) |
162 | // assumption that information related to register def/uses can be obtained |
163 | // from MCInstrDesc. |
164 | // |
165 | // However class MCInstrDesc is used to describe MachineInstr objects and not |
166 | // MCInst objects. To be more specific, MCInstrDesc objects are opcode |
167 | // descriptors that are automatically generated via tablegen based on the |
168 | // instruction set information available from the target .td files. That |
169 | // means, the number of (explicit) definitions according to MCInstrDesc always |
170 | // matches the cardinality of the `(outs)` set in tablegen. |
171 | // |
172 | // By constructions, definitions must appear first in the operand sequence of |
173 | // a MachineInstr. Also, the (outs) sequence is preserved (example: the first |
174 | // element in the outs set is the first operand in the corresponding |
175 | // MachineInstr). That's the reason why MCInstrDesc only needs to declare the |
176 | // total number of register definitions, and not where those definitions are |
177 | // in the machine operand sequence. |
178 | // |
179 | // Unfortunately, it is not safe to use the information from MCInstrDesc to |
180 | // also describe MCInst objects. An MCInst object can be obtained from a |
181 | // MachineInstr through a lowering step which may restructure the operand |
182 | // sequence (and even remove or introduce new operands). So, there is a high |
183 | // risk that the lowering step breaks the assumptions that register |
184 | // definitions are always at the beginning of the machine operand sequence. |
185 | // |
186 | // This is a fundamental problem, and it is still an open problem. Essentially |
187 | // we have to find a way to correlate def/use operands of a MachineInstr to |
188 | // operands of an MCInst. Otherwise, we cannot correctly reconstruct data |
189 | // dependencies, nor we can correctly interpret the scheduling model, which |
190 | // heavily uses machine operand indices to define processor read-advance |
191 | // information, and to identify processor write resources. Essentially, we |
192 | // either need something like a MCInstrDesc, but for MCInst, or a way |
193 | // to map MCInst operands back to MachineInstr operands. |
194 | // |
195 | // Unfortunately, we don't have that information now. So, this prototype |
196 | // currently work under the strong assumption that we can always safely trust |
197 | // the content of an MCInstrDesc. For example, we can query a MCInstrDesc to |
198 | // obtain the number of explicit and implicit register defintions. We also |
199 | // assume that register definitions always come first in the operand sequence. |
200 | // This last assumption usually makes sense for MachineInstr, where register |
201 | // definitions always appear at the beginning of the operands sequence. In |
202 | // reality, these assumptions could be broken by the lowering step, which can |
203 | // decide to lay out operands in a different order than the original order of |
204 | // operand as specified by the MachineInstr. |
205 | // |
206 | // Things get even more complicated in the presence of "optional" register |
207 | // definitions. For MachineInstr, optional register definitions are always at |
208 | // the end of the operand sequence. Some ARM instructions that may update the |
209 | // status flags specify that register as a optional operand. Since we don't |
210 | // have operand descriptors for MCInst, we assume for now that the optional |
211 | // definition is always the last operand of a MCInst. Again, this assumption |
212 | // may be okay for most targets. However, there is no guarantee that targets |
213 | // would respect that. |
214 | // |
215 | // In conclusion: these are for now the strong assumptions made by the tool: |
216 | // * The number of explicit and implicit register definitions in a MCInst |
217 | // matches the number of explicit and implicit definitions according to |
218 | // the opcode descriptor (MCInstrDesc). |
219 | // * Register definitions take precedence over register uses in the operands |
220 | // list. |
221 | // * If an opcode specifies an optional definition, then the optional |
222 | // definition is always the last operand in the sequence, and it can be |
223 | // set to zero (i.e. "no register"). |
224 | // |
225 | // These assumptions work quite well for most out-of-order in-tree targets |
226 | // like x86. This is mainly because the vast majority of instructions is |
227 | // expanded to MCInst using a straightforward lowering logic that preserves |
228 | // the ordering of the operands. |
229 | // |
230 | // In the longer term, we need to find a proper solution for this issue. |
231 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
232 | unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs(); |
233 | unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries; |
234 | unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs; |
235 | if (MCDesc.hasOptionalDef()) |
236 | TotalDefs++; |
237 | ID.Writes.resize(TotalDefs); |
238 | // Iterate over the operands list, and skip non-register operands. |
239 | // The first NumExplictDefs register operands are expected to be register |
240 | // definitions. |
241 | unsigned CurrentDef = 0; |
242 | unsigned i = 0; |
243 | for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) { |
244 | const MCOperand &Op = MCI.getOperand(i); |
245 | if (!Op.isReg()) |
246 | continue; |
247 | |
248 | WriteDescriptor &Write = ID.Writes[CurrentDef]; |
249 | Write.OpIndex = i; |
250 | if (CurrentDef < NumWriteLatencyEntries) { |
251 | const MCWriteLatencyEntry &WLE = |
252 | *STI.getWriteLatencyEntry(&SCDesc, CurrentDef); |
253 | // Conservatively default to MaxLatency. |
254 | Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles; |
255 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
256 | } else { |
257 | // Assign a default latency for this write. |
258 | Write.Latency = ID.MaxLatency; |
259 | Write.SClassOrWriteResourceID = 0; |
260 | } |
261 | Write.FullyUpdatesSuperRegs = FullyUpdatesSuperRegisters; |
262 | Write.IsOptionalDef = false; |
263 | DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\tOpIdx=" << Write .OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
264 | dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\tOpIdx=" << Write .OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
265 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\tOpIdx=" << Write .OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
266 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\tOpIdx=" << Write .OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
267 | CurrentDef++; |
268 | } |
269 | |
270 | if (CurrentDef != NumExplicitDefs) |
271 | llvm::report_fatal_error( |
272 | "error: Expected more register operand definitions. "); |
273 | |
274 | CurrentDef = 0; |
Value stored to 'CurrentDef' is never read | |
275 | for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) { |
276 | unsigned Index = NumExplicitDefs + CurrentDef; |
277 | WriteDescriptor &Write = ID.Writes[Index]; |
278 | Write.OpIndex = -1; |
279 | Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef]; |
280 | if (Index < NumWriteLatencyEntries) { |
281 | const MCWriteLatencyEntry &WLE = |
282 | *STI.getWriteLatencyEntry(&SCDesc, Index); |
283 | // Conservatively default to MaxLatency. |
284 | Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles; |
285 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
286 | } else { |
287 | // Assign a default latency for this write. |
288 | Write.Latency = ID.MaxLatency; |
289 | Write.SClassOrWriteResourceID = 0; |
290 | } |
291 | |
292 | Write.IsOptionalDef = false; |
293 | assert(Write.RegisterID != 0 && "Expected a valid phys register!")(static_cast <bool> (Write.RegisterID != 0 && "Expected a valid phys register!" ) ? void (0) : __assert_fail ("Write.RegisterID != 0 && \"Expected a valid phys register!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 293, __extension__ __PRETTY_FUNCTION__)); |
294 | DEBUG(dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg="do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg=" << Write.RegisterID << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; } } while (false ) |
295 | << Write.RegisterID << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg=" << Write.RegisterID << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; } } while (false ) |
296 | << ", WriteResourceID=" << Write.SClassOrWriteResourceIDdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg=" << Write.RegisterID << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; } } while (false ) |
297 | << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg=" << Write.RegisterID << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; } } while (false ); |
298 | } |
299 | |
300 | if (MCDesc.hasOptionalDef()) { |
301 | // Always assume that the optional definition is the last operand of the |
302 | // MCInst sequence. |
303 | const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1); |
304 | if (i == MCI.getNumOperands() || !Op.isReg()) |
305 | llvm::report_fatal_error( |
306 | "error: expected a register operand for an optional " |
307 | "definition. Instruction has not be correctly analyzed.\n", |
308 | false); |
309 | |
310 | WriteDescriptor &Write = ID.Writes[TotalDefs - 1]; |
311 | Write.OpIndex = MCI.getNumOperands() - 1; |
312 | // Assign a default latency for this write. |
313 | Write.Latency = ID.MaxLatency; |
314 | Write.SClassOrWriteResourceID = 0; |
315 | Write.IsOptionalDef = true; |
316 | } |
317 | } |
318 | |
319 | static void populateReads(InstrDesc &ID, const MCInst &MCI, |
320 | const MCInstrDesc &MCDesc, |
321 | const MCSchedClassDesc &SCDesc, |
322 | const MCSubtargetInfo &STI) { |
323 | unsigned SchedClassID = MCDesc.getSchedClass(); |
324 | bool HasReadAdvanceEntries = SCDesc.NumReadAdvanceEntries > 0; |
325 | |
326 | unsigned i = 0; |
327 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
328 | // Skip explicit definitions. |
329 | for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) { |
330 | const MCOperand &Op = MCI.getOperand(i); |
331 | if (Op.isReg()) |
332 | NumExplicitDefs--; |
333 | } |
334 | |
335 | if (NumExplicitDefs) |
336 | llvm::report_fatal_error( |
337 | "error: Expected more register operand definitions. ", false); |
338 | |
339 | unsigned NumExplicitUses = MCI.getNumOperands() - i; |
340 | unsigned NumImplicitUses = MCDesc.getNumImplicitUses(); |
341 | if (MCDesc.hasOptionalDef()) { |
342 | assert(NumExplicitUses)(static_cast <bool> (NumExplicitUses) ? void (0) : __assert_fail ("NumExplicitUses", "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 342, __extension__ __PRETTY_FUNCTION__)); |
343 | NumExplicitUses--; |
344 | } |
345 | unsigned TotalUses = NumExplicitUses + NumImplicitUses; |
346 | if (!TotalUses) |
347 | return; |
348 | |
349 | ID.Reads.resize(TotalUses); |
350 | for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) { |
351 | ReadDescriptor &Read = ID.Reads[CurrentUse]; |
352 | Read.OpIndex = i + CurrentUse; |
353 | Read.UseIndex = CurrentUse; |
354 | Read.HasReadAdvanceEntries = HasReadAdvanceEntries; |
355 | Read.SchedClassID = SchedClassID; |
356 | DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndex)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Read.OpIndex ; } } while (false); |
357 | } |
358 | |
359 | for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) { |
360 | ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse]; |
361 | Read.OpIndex = -1; |
362 | Read.UseIndex = NumExplicitUses + CurrentUse; |
363 | Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse]; |
364 | Read.HasReadAdvanceEntries = HasReadAdvanceEntries; |
365 | Read.SchedClassID = SchedClassID; |
366 | DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Read.OpIndex << ", RegisterID=" << Read.RegisterID << '\n' ; } } while (false) |
367 | << ", RegisterID=" << Read.RegisterID << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tOpIdx=" << Read.OpIndex << ", RegisterID=" << Read.RegisterID << '\n' ; } } while (false); |
368 | } |
369 | } |
370 | |
371 | void InstrBuilder::createInstrDescImpl(const MCInst &MCI) { |
372 | assert(STI.getSchedModel().hasInstrSchedModel() &&(static_cast <bool> (STI.getSchedModel().hasInstrSchedModel () && "Itineraries are not yet supported!") ? void (0 ) : __assert_fail ("STI.getSchedModel().hasInstrSchedModel() && \"Itineraries are not yet supported!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 373, __extension__ __PRETTY_FUNCTION__)) |
373 | "Itineraries are not yet supported!")(static_cast <bool> (STI.getSchedModel().hasInstrSchedModel () && "Itineraries are not yet supported!") ? void (0 ) : __assert_fail ("STI.getSchedModel().hasInstrSchedModel() && \"Itineraries are not yet supported!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 373, __extension__ __PRETTY_FUNCTION__)); |
374 | |
375 | unsigned short Opcode = MCI.getOpcode(); |
376 | // Obtain the instruction descriptor from the opcode. |
377 | const MCInstrDesc &MCDesc = MCII.get(Opcode); |
378 | const MCSchedModel &SM = STI.getSchedModel(); |
379 | |
380 | // Then obtain the scheduling class information from the instruction. |
381 | const MCSchedClassDesc &SCDesc = |
382 | *SM.getSchedClassDesc(MCDesc.getSchedClass()); |
383 | |
384 | // Create a new empty descriptor. |
385 | std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>(); |
386 | |
387 | if (SCDesc.isVariant()) { |
388 | errs() << "warning: don't know how to model variant opcodes.\n" |
389 | << "note: assume 1 micro opcode.\n"; |
390 | ID->NumMicroOps = 1U; |
391 | } else { |
392 | ID->NumMicroOps = SCDesc.NumMicroOps; |
393 | } |
394 | |
395 | if (MCDesc.isCall()) { |
396 | // We don't correctly model calls. |
397 | errs() << "warning: found a call in the input assembly sequence.\n" |
398 | << "note: call instructions are not correctly modeled. Assume a " |
399 | "latency of 100cy.\n"; |
400 | } |
401 | |
402 | if (MCDesc.isReturn()) { |
403 | errs() << "warning: found a return instruction in the input assembly " |
404 | "sequence.\n" |
405 | << "note: program counter updates are ignored.\n"; |
406 | } |
407 | |
408 | ID->MayLoad = MCDesc.mayLoad(); |
409 | ID->MayStore = MCDesc.mayStore(); |
410 | ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects(); |
411 | |
412 | initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks); |
413 | populateWrites(*ID, MCI, MCDesc, SCDesc, STI); |
414 | populateReads(*ID, MCI, MCDesc, SCDesc, STI); |
415 | |
416 | DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tMaxLatency=" << ID ->MaxLatency << '\n'; } } while (false); |
417 | DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tNumMicroOps=" << ID ->NumMicroOps << '\n'; } } while (false); |
418 | |
419 | // Now add the new descriptor. |
420 | Descriptors[Opcode] = std::move(ID); |
421 | } |
422 | |
423 | const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { |
424 | if (Descriptors.find_as(MCI.getOpcode()) == Descriptors.end()) |
425 | createInstrDescImpl(MCI); |
426 | return *Descriptors[MCI.getOpcode()]; |
427 | } |
428 | |
429 | std::unique_ptr<Instruction> |
430 | InstrBuilder::createInstruction(unsigned Idx, const MCInst &MCI) { |
431 | const InstrDesc &D = getOrCreateInstrDesc(MCI); |
432 | std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D); |
433 | |
434 | // Populate Reads first. |
435 | for (const ReadDescriptor &RD : D.Reads) { |
436 | int RegID = -1; |
437 | if (RD.OpIndex != -1) { |
438 | // explicit read. |
439 | const MCOperand &Op = MCI.getOperand(RD.OpIndex); |
440 | // Skip non-register operands. |
441 | if (!Op.isReg()) |
442 | continue; |
443 | RegID = Op.getReg(); |
444 | } else { |
445 | // Implicit read. |
446 | RegID = RD.RegisterID; |
447 | } |
448 | |
449 | // Skip invalid register operands. |
450 | if (!RegID) |
451 | continue; |
452 | |
453 | // Okay, this is a register operand. Create a ReadState for it. |
454 | assert(RegID > 0 && "Invalid register ID found!")(static_cast <bool> (RegID > 0 && "Invalid register ID found!" ) ? void (0) : __assert_fail ("RegID > 0 && \"Invalid register ID found!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 454, __extension__ __PRETTY_FUNCTION__)); |
455 | NewIS->getUses().emplace_back(llvm::make_unique<ReadState>(RD, RegID)); |
456 | } |
457 | |
458 | // Now populate writes. |
459 | for (const WriteDescriptor &WD : D.Writes) { |
460 | unsigned RegID = |
461 | WD.OpIndex == -1 ? WD.RegisterID : MCI.getOperand(WD.OpIndex).getReg(); |
462 | // Check if this is a optional definition that references NoReg. |
463 | if (WD.IsOptionalDef && !RegID) |
464 | continue; |
465 | |
466 | assert(RegID && "Expected a valid register ID!")(static_cast <bool> (RegID && "Expected a valid register ID!" ) ? void (0) : __assert_fail ("RegID && \"Expected a valid register ID!\"" , "/build/llvm-toolchain-snapshot-7~svn329677/tools/llvm-mca/InstrBuilder.cpp" , 466, __extension__ __PRETTY_FUNCTION__)); |
467 | NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(WD, RegID)); |
468 | } |
469 | |
470 | return NewIS; |
471 | } |
472 | } // namespace mca |