File: | tools/llvm-mca/lib/InstrBuilder.cpp |
Warning: | line 230, 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/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 mca { |
26 | |
27 | using namespace llvm; |
28 | |
29 | InstrBuilder::InstrBuilder(const llvm::MCSubtargetInfo &sti, |
30 | const llvm::MCInstrInfo &mcii, |
31 | const llvm::MCRegisterInfo &mri, |
32 | const llvm::MCInstrAnalysis &mcia) |
33 | : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia) { |
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 | for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) { |
60 | const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I; |
61 | const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx); |
62 | uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx]; |
63 | if (PR.BufferSize != -1) |
64 | ID.Buffers.push_back(Mask); |
65 | CycleSegment RCy(0, PRE->Cycles, false); |
66 | Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy))); |
67 | if (PR.SuperIdx) { |
68 | uint64_t Super = ProcResourceMasks[PR.SuperIdx]; |
69 | SuperResources[Super] += PRE->Cycles; |
70 | } |
71 | } |
72 | |
73 | // Sort elements by mask popcount, so that we prioritize resource units over |
74 | // resource groups, and smaller groups over larger groups. |
75 | sort(Worklist, [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) { |
76 | unsigned popcntA = countPopulation(A.first); |
77 | unsigned popcntB = countPopulation(B.first); |
78 | if (popcntA < popcntB) |
79 | return true; |
80 | if (popcntA > popcntB) |
81 | return false; |
82 | return A.first < B.first; |
83 | }); |
84 | |
85 | uint64_t UsedResourceUnits = 0; |
86 | |
87 | // Remove cycles contributed by smaller resources. |
88 | for (unsigned I = 0, E = Worklist.size(); I < E; ++I) { |
89 | ResourcePlusCycles &A = Worklist[I]; |
90 | if (!A.second.size()) { |
91 | A.second.NumUnits = 0; |
92 | A.second.setReserved(); |
93 | ID.Resources.emplace_back(A); |
94 | continue; |
95 | } |
96 | |
97 | ID.Resources.emplace_back(A); |
98 | uint64_t NormalizedMask = A.first; |
99 | if (countPopulation(A.first) == 1) { |
100 | UsedResourceUnits |= A.first; |
101 | } else { |
102 | // Remove the leading 1 from the resource group mask. |
103 | NormalizedMask ^= PowerOf2Floor(NormalizedMask); |
104 | } |
105 | |
106 | for (unsigned J = I + 1; J < E; ++J) { |
107 | ResourcePlusCycles &B = Worklist[J]; |
108 | if ((NormalizedMask & B.first) == NormalizedMask) { |
109 | B.second.CS.subtract(A.second.size() - SuperResources[A.first]); |
110 | if (countPopulation(B.first) > 1) |
111 | B.second.NumUnits++; |
112 | } |
113 | } |
114 | } |
115 | |
116 | // A SchedWrite may specify a number of cycles in which a resource group |
117 | // is reserved. For example (on target x86; cpu Haswell): |
118 | // |
119 | // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> { |
120 | // let ResourceCycles = [2, 2, 3]; |
121 | // } |
122 | // |
123 | // This means: |
124 | // Resource units HWPort0 and HWPort1 are both used for 2cy. |
125 | // Resource group HWPort01 is the union of HWPort0 and HWPort1. |
126 | // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01 |
127 | // will not be usable for 2 entire cycles from instruction issue. |
128 | // |
129 | // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency |
130 | // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an |
131 | // extra delay on top of the 2 cycles latency. |
132 | // During those extra cycles, HWPort01 is not usable by other instructions. |
133 | for (ResourcePlusCycles &RPC : ID.Resources) { |
134 | if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) { |
135 | // Remove the leading 1 from the resource group mask. |
136 | uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first); |
137 | if ((Mask & UsedResourceUnits) == Mask) |
138 | RPC.second.setReserved(); |
139 | } |
140 | } |
141 | |
142 | 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) |
143 | 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) |
144 | 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) |
145 | 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) |
146 | 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) |
147 | })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); |
148 | } |
149 | |
150 | static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc, |
151 | const MCSchedClassDesc &SCDesc, |
152 | const MCSubtargetInfo &STI) { |
153 | if (MCDesc.isCall()) { |
154 | // We cannot estimate how long this call will take. |
155 | // Artificially set an arbitrarily high latency (100cy). |
156 | ID.MaxLatency = 100U; |
157 | return; |
158 | } |
159 | |
160 | int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
161 | // If latency is unknown, then conservatively assume a MaxLatency of 100cy. |
162 | ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency); |
163 | } |
164 | |
165 | Error InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI, |
166 | unsigned SchedClassID) { |
167 | const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode()); |
168 | const MCSchedModel &SM = STI.getSchedModel(); |
169 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
170 | |
171 | // These are for now the (strong) assumptions made by this algorithm: |
172 | // * The number of explicit and implicit register definitions in a MCInst |
173 | // matches the number of explicit and implicit definitions according to |
174 | // the opcode descriptor (MCInstrDesc). |
175 | // * Register definitions take precedence over register uses in the operands |
176 | // list. |
177 | // * If an opcode specifies an optional definition, then the optional |
178 | // definition is always the last operand in the sequence, and it can be |
179 | // set to zero (i.e. "no register"). |
180 | // |
181 | // These assumptions work quite well for most out-of-order in-tree targets |
182 | // like x86. This is mainly because the vast majority of instructions is |
183 | // expanded to MCInst using a straightforward lowering logic that preserves |
184 | // the ordering of the operands. |
185 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
186 | unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs(); |
187 | unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries; |
188 | unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs; |
189 | if (MCDesc.hasOptionalDef()) |
190 | TotalDefs++; |
191 | ID.Writes.resize(TotalDefs); |
192 | // Iterate over the operands list, and skip non-register operands. |
193 | // The first NumExplictDefs register operands are expected to be register |
194 | // definitions. |
195 | unsigned CurrentDef = 0; |
196 | unsigned i = 0; |
197 | for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) { |
198 | const MCOperand &Op = MCI.getOperand(i); |
199 | if (!Op.isReg()) |
200 | continue; |
201 | |
202 | WriteDescriptor &Write = ID.Writes[CurrentDef]; |
203 | Write.OpIndex = i; |
204 | if (CurrentDef < NumWriteLatencyEntries) { |
205 | const MCWriteLatencyEntry &WLE = |
206 | *STI.getWriteLatencyEntry(&SCDesc, CurrentDef); |
207 | // Conservatively default to MaxLatency. |
208 | Write.Latency = |
209 | WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles); |
210 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
211 | } else { |
212 | // Assign a default latency for this write. |
213 | Write.Latency = ID.MaxLatency; |
214 | Write.SClassOrWriteResourceID = 0; |
215 | } |
216 | Write.IsOptionalDef = false; |
217 | 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) |
218 | 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) |
219 | << ", 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) |
220 | << ", 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) |
221 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
222 | CurrentDef++; |
223 | } |
224 | |
225 | if (CurrentDef != NumExplicitDefs) { |
226 | return make_error<InstructionError<MCInst>>( |
227 | "Expected more register operand definitions.", MCI); |
228 | } |
229 | |
230 | CurrentDef = 0; |
Value stored to 'CurrentDef' is never read | |
231 | for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) { |
232 | unsigned Index = NumExplicitDefs + CurrentDef; |
233 | WriteDescriptor &Write = ID.Writes[Index]; |
234 | Write.OpIndex = ~CurrentDef; |
235 | Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef]; |
236 | if (Index < NumWriteLatencyEntries) { |
237 | const MCWriteLatencyEntry &WLE = |
238 | *STI.getWriteLatencyEntry(&SCDesc, Index); |
239 | // Conservatively default to MaxLatency. |
240 | Write.Latency = |
241 | WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles); |
242 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
243 | } else { |
244 | // Assign a default latency for this write. |
245 | Write.Latency = ID.MaxLatency; |
246 | Write.SClassOrWriteResourceID = 0; |
247 | } |
248 | |
249 | Write.IsOptionalDef = false; |
250 | 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~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 250, __PRETTY_FUNCTION__)); |
251 | LLVM_DEBUG({do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
252 | dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
253 | << ", PhysReg=" << MRI.getName(Write.RegisterID)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
254 | << ", Latency=" << Write.Latencydo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
255 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false) |
256 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { { dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex << ", PhysReg=" << MRI.getName(Write .RegisterID) << ", Latency=" << Write.Latency << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; }; } } while (false); |
257 | } |
258 | |
259 | if (MCDesc.hasOptionalDef()) { |
260 | // Always assume that the optional definition is the last operand of the |
261 | // MCInst sequence. |
262 | const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1); |
263 | if (i == MCI.getNumOperands() || !Op.isReg()) { |
264 | std::string Message = |
265 | "expected a register operand for an optional definition. Instruction " |
266 | "has not been correctly analyzed."; |
267 | return make_error<InstructionError<MCInst>>(Message, MCI); |
268 | } |
269 | |
270 | WriteDescriptor &Write = ID.Writes[TotalDefs - 1]; |
271 | Write.OpIndex = MCI.getNumOperands() - 1; |
272 | // Assign a default latency for this write. |
273 | Write.Latency = ID.MaxLatency; |
274 | Write.SClassOrWriteResourceID = 0; |
275 | Write.IsOptionalDef = true; |
276 | } |
277 | |
278 | return ErrorSuccess(); |
279 | } |
280 | |
281 | Error InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI, |
282 | unsigned SchedClassID) { |
283 | const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode()); |
284 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
285 | |
286 | // Skip explicit definitions. |
287 | unsigned i = 0; |
288 | for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) { |
289 | const MCOperand &Op = MCI.getOperand(i); |
290 | if (Op.isReg()) |
291 | NumExplicitDefs--; |
292 | } |
293 | |
294 | if (NumExplicitDefs) { |
295 | return make_error<InstructionError<MCInst>>( |
296 | "Expected more register operand definitions.", MCI); |
297 | } |
298 | |
299 | unsigned NumExplicitUses = MCI.getNumOperands() - i; |
300 | unsigned NumImplicitUses = MCDesc.getNumImplicitUses(); |
301 | if (MCDesc.hasOptionalDef()) { |
302 | assert(NumExplicitUses)((NumExplicitUses) ? static_cast<void> (0) : __assert_fail ("NumExplicitUses", "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 302, __PRETTY_FUNCTION__)); |
303 | NumExplicitUses--; |
304 | } |
305 | unsigned TotalUses = NumExplicitUses + NumImplicitUses; |
306 | if (!TotalUses) |
307 | return ErrorSuccess(); |
308 | |
309 | ID.Reads.resize(TotalUses); |
310 | for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) { |
311 | ReadDescriptor &Read = ID.Reads[CurrentUse]; |
312 | Read.OpIndex = i + CurrentUse; |
313 | Read.UseIndex = CurrentUse; |
314 | Read.SchedClassID = SchedClassID; |
315 | 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) |
316 | << ", 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); |
317 | } |
318 | |
319 | for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) { |
320 | ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse]; |
321 | Read.OpIndex = ~CurrentUse; |
322 | Read.UseIndex = NumExplicitUses + CurrentUse; |
323 | Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse]; |
324 | Read.SchedClassID = SchedClassID; |
325 | LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", RegisterID="do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use] OpIdx=" << Read .OpIndex << ", RegisterID=" << MRI.getName(Read.RegisterID ) << '\n'; } } while (false) |
326 | << MRI.getName(Read.RegisterID) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\t[Use] OpIdx=" << Read .OpIndex << ", RegisterID=" << MRI.getName(Read.RegisterID ) << '\n'; } } while (false); |
327 | } |
328 | return ErrorSuccess(); |
329 | } |
330 | |
331 | Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID, |
332 | const MCInst &MCI) const { |
333 | if (ID.NumMicroOps != 0) |
334 | return ErrorSuccess(); |
335 | |
336 | bool UsesMemory = ID.MayLoad || ID.MayStore; |
337 | bool UsesBuffers = !ID.Buffers.empty(); |
338 | bool UsesResources = !ID.Resources.empty(); |
339 | if (!UsesMemory && !UsesBuffers && !UsesResources) |
340 | return ErrorSuccess(); |
341 | |
342 | StringRef Message; |
343 | if (UsesMemory) { |
344 | Message = "found an inconsistent instruction that decodes " |
345 | "into zero opcodes and that consumes load/store " |
346 | "unit resources."; |
347 | } else { |
348 | Message = "found an inconsistent instruction that decodes " |
349 | "to zero opcodes and that consumes scheduler " |
350 | "resources."; |
351 | } |
352 | |
353 | return make_error<InstructionError<MCInst>>(Message, MCI); |
354 | } |
355 | |
356 | Expected<const InstrDesc &> |
357 | InstrBuilder::createInstrDescImpl(const MCInst &MCI) { |
358 | 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~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 359, __PRETTY_FUNCTION__)) |
359 | "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~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 359, __PRETTY_FUNCTION__)); |
360 | |
361 | // Obtain the instruction descriptor from the opcode. |
362 | unsigned short Opcode = MCI.getOpcode(); |
363 | const MCInstrDesc &MCDesc = MCII.get(Opcode); |
364 | const MCSchedModel &SM = STI.getSchedModel(); |
365 | |
366 | // Then obtain the scheduling class information from the instruction. |
367 | unsigned SchedClassID = MCDesc.getSchedClass(); |
368 | unsigned CPUID = SM.getProcessorID(); |
369 | |
370 | // Try to solve variant scheduling classes. |
371 | if (SchedClassID) { |
372 | while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant()) |
373 | SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID); |
374 | |
375 | if (!SchedClassID) { |
376 | return make_error<InstructionError<MCInst>>( |
377 | "unable to resolve scheduling class for write variant.", MCI); |
378 | } |
379 | } |
380 | |
381 | // Check if this instruction is supported. Otherwise, report an error. |
382 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
383 | if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) { |
384 | return make_error<InstructionError<MCInst>>( |
385 | "found an unsupported instruction in the input assembly sequence.", |
386 | MCI); |
387 | } |
388 | |
389 | // Create a new empty descriptor. |
390 | std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>(); |
391 | ID->NumMicroOps = SCDesc.NumMicroOps; |
392 | |
393 | if (MCDesc.isCall()) { |
394 | // We don't correctly model calls. |
395 | WithColor::warning() << "found a call in the input assembly sequence.\n"; |
396 | WithColor::note() << "call instructions are not correctly modeled. " |
397 | << "Assume a latency of 100cy.\n"; |
398 | } |
399 | |
400 | if (MCDesc.isReturn()) { |
401 | WithColor::warning() << "found a return instruction in the input" |
402 | << " assembly sequence.\n"; |
403 | WithColor::note() << "program counter updates are ignored.\n"; |
404 | } |
405 | |
406 | ID->MayLoad = MCDesc.mayLoad(); |
407 | ID->MayStore = MCDesc.mayStore(); |
408 | ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects(); |
409 | |
410 | initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks); |
411 | computeMaxLatency(*ID, MCDesc, SCDesc, STI); |
412 | if (auto Err = populateWrites(*ID, MCI, SchedClassID)) |
413 | return std::move(Err); |
414 | if (auto Err = populateReads(*ID, MCI, SchedClassID)) |
415 | return std::move(Err); |
416 | |
417 | LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tMaxLatency=" << ID ->MaxLatency << '\n'; } } while (false); |
418 | LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("llvm-mca")) { dbgs() << "\t\tNumMicroOps=" << ID ->NumMicroOps << '\n'; } } while (false); |
419 | |
420 | // Sanity check on the instruction descriptor. |
421 | if (Error Err = verifyInstrDesc(*ID, MCI)) |
422 | return std::move(Err); |
423 | |
424 | // Now add the new descriptor. |
425 | SchedClassID = MCDesc.getSchedClass(); |
426 | if (!SM.getSchedClassDesc(SchedClassID)->isVariant()) { |
427 | Descriptors[MCI.getOpcode()] = std::move(ID); |
428 | return *Descriptors[MCI.getOpcode()]; |
429 | } |
430 | |
431 | VariantDescriptors[&MCI] = std::move(ID); |
432 | return *VariantDescriptors[&MCI]; |
433 | } |
434 | |
435 | Expected<const InstrDesc &> |
436 | InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { |
437 | if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end()) |
438 | return *Descriptors[MCI.getOpcode()]; |
439 | |
440 | if (VariantDescriptors.find(&MCI) != VariantDescriptors.end()) |
441 | return *VariantDescriptors[&MCI]; |
442 | |
443 | return createInstrDescImpl(MCI); |
444 | } |
445 | |
446 | Expected<std::unique_ptr<Instruction>> |
447 | InstrBuilder::createInstruction(const MCInst &MCI) { |
448 | Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI); |
449 | if (!DescOrErr) |
450 | return DescOrErr.takeError(); |
451 | const InstrDesc &D = *DescOrErr; |
452 | std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D); |
453 | |
454 | // Check if this is a dependency breaking instruction. |
455 | APInt Mask; |
456 | |
457 | unsigned ProcID = STI.getSchedModel().getProcessorID(); |
458 | bool IsZeroIdiom = MCIA.isZeroIdiom(MCI, Mask, ProcID); |
459 | bool IsDepBreaking = |
460 | IsZeroIdiom || MCIA.isDependencyBreaking(MCI, Mask, ProcID); |
461 | if (MCIA.isOptimizableRegisterMove(MCI, ProcID)) |
462 | NewIS->setOptimizableMove(); |
463 | |
464 | // Initialize Reads first. |
465 | for (const ReadDescriptor &RD : D.Reads) { |
466 | int RegID = -1; |
467 | if (!RD.isImplicitRead()) { |
468 | // explicit read. |
469 | const MCOperand &Op = MCI.getOperand(RD.OpIndex); |
470 | // Skip non-register operands. |
471 | if (!Op.isReg()) |
472 | continue; |
473 | RegID = Op.getReg(); |
474 | } else { |
475 | // Implicit read. |
476 | RegID = RD.RegisterID; |
477 | } |
478 | |
479 | // Skip invalid register operands. |
480 | if (!RegID) |
481 | continue; |
482 | |
483 | // Okay, this is a register operand. Create a ReadState for it. |
484 | 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~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 484, __PRETTY_FUNCTION__)); |
485 | NewIS->getUses().emplace_back(RD, RegID); |
486 | ReadState &RS = NewIS->getUses().back(); |
487 | |
488 | if (IsDepBreaking) { |
489 | // A mask of all zeroes means: explicit input operands are not |
490 | // independent. |
491 | if (Mask.isNullValue()) { |
492 | if (!RD.isImplicitRead()) |
493 | RS.setIndependentFromDef(); |
494 | } else { |
495 | // Check if this register operand is independent according to `Mask`. |
496 | // Note that Mask may not have enough bits to describe all explicit and |
497 | // implicit input operands. If this register operand doesn't have a |
498 | // corresponding bit in Mask, then conservatively assume that it is |
499 | // dependent. |
500 | if (Mask.getBitWidth() > RD.UseIndex) { |
501 | // Okay. This map describe register use `RD.UseIndex`. |
502 | if (Mask[RD.UseIndex]) |
503 | RS.setIndependentFromDef(); |
504 | } |
505 | } |
506 | } |
507 | } |
508 | |
509 | // Early exit if there are no writes. |
510 | if (D.Writes.empty()) |
511 | return std::move(NewIS); |
512 | |
513 | // Track register writes that implicitly clear the upper portion of the |
514 | // underlying super-registers using an APInt. |
515 | APInt WriteMask(D.Writes.size(), 0); |
516 | |
517 | // Now query the MCInstrAnalysis object to obtain information about which |
518 | // register writes implicitly clear the upper portion of a super-register. |
519 | MCIA.clearsSuperRegisters(MRI, MCI, WriteMask); |
520 | |
521 | // Initialize writes. |
522 | unsigned WriteIndex = 0; |
523 | for (const WriteDescriptor &WD : D.Writes) { |
524 | unsigned RegID = WD.isImplicitWrite() ? WD.RegisterID |
525 | : MCI.getOperand(WD.OpIndex).getReg(); |
526 | // Check if this is a optional definition that references NoReg. |
527 | if (WD.IsOptionalDef && !RegID) { |
528 | ++WriteIndex; |
529 | continue; |
530 | } |
531 | |
532 | 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~svn345461/tools/llvm-mca/lib/InstrBuilder.cpp" , 532, __PRETTY_FUNCTION__)); |
533 | NewIS->getDefs().emplace_back( |
534 | WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex], |
535 | /* WritesZero */ IsZeroIdiom); |
536 | ++WriteIndex; |
537 | } |
538 | |
539 | return std::move(NewIS); |
540 | } |
541 | } // namespace mca |