blob: a745e1a6150960cb511583229529c3246842c275 [file] [log] [blame]
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +00001//===--------------------- 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"
Andrea Di Biagio2008c7c2018-06-04 12:23:07 +000016#include "llvm/ADT/DenseMap.h"
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000017#include "llvm/MC/MCInst.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/raw_ostream.h"
Andrea Di Biagio24fb4fc2018-05-04 13:52:12 +000020#include "llvm/Support/WithColor.h"
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000021
22#define DEBUG_TYPE "llvm-mca"
23
24namespace mca {
25
26using namespace llvm;
27
Andrea Di Biagio94fafdf2018-03-24 16:05:36 +000028static void initializeUsedResources(InstrDesc &ID,
29 const MCSchedClassDesc &SCDesc,
30 const MCSubtargetInfo &STI,
31 ArrayRef<uint64_t> ProcResourceMasks) {
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000032 const MCSchedModel &SM = STI.getSchedModel();
33
34 // Populate resources consumed.
35 using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
36 std::vector<ResourcePlusCycles> Worklist;
Andrea Di Biagio2008c7c2018-06-04 12:23:07 +000037
38 // Track cycles contributed by resources that are in a "Super" relationship.
39 // This is required if we want to correctly match the behavior of method
40 // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
41 // of "consumed" processor resources and resource cycles, the logic in
42 // ExpandProcResource() doesn't update the number of resource cycles
43 // contributed by a "Super" resource to a group.
44 // We need to take this into account when we find that a processor resource is
45 // part of a group, and it is also used as the "Super" of other resources.
46 // This map stores the number of cycles contributed by sub-resources that are
47 // part of a "Super" resource. The key value is the "Super" resource mask ID.
48 DenseMap<uint64_t, unsigned> SuperResources;
49
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000050 for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
51 const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
52 const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
53 uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
54 if (PR.BufferSize != -1)
55 ID.Buffers.push_back(Mask);
56 CycleSegment RCy(0, PRE->Cycles, false);
57 Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
Andrea Di Biagio2008c7c2018-06-04 12:23:07 +000058 if (PR.SuperIdx) {
59 uint64_t Super = ProcResourceMasks[PR.SuperIdx];
60 SuperResources[Super] += PRE->Cycles;
61 }
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000062 }
63
64 // Sort elements by mask popcount, so that we prioritize resource units over
65 // resource groups, and smaller groups over larger groups.
Mandeep Singh Grang8db564e2018-04-01 21:24:53 +000066 llvm::sort(Worklist.begin(), Worklist.end(),
67 [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
68 unsigned popcntA = countPopulation(A.first);
69 unsigned popcntB = countPopulation(B.first);
70 if (popcntA < popcntB)
71 return true;
72 if (popcntA > popcntB)
73 return false;
74 return A.first < B.first;
75 });
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000076
77 uint64_t UsedResourceUnits = 0;
78
79 // Remove cycles contributed by smaller resources.
80 for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
81 ResourcePlusCycles &A = Worklist[I];
82 if (!A.second.size()) {
83 A.second.NumUnits = 0;
84 A.second.setReserved();
85 ID.Resources.emplace_back(A);
86 continue;
87 }
88
89 ID.Resources.emplace_back(A);
90 uint64_t NormalizedMask = A.first;
91 if (countPopulation(A.first) == 1) {
92 UsedResourceUnits |= A.first;
93 } else {
94 // Remove the leading 1 from the resource group mask.
95 NormalizedMask ^= PowerOf2Floor(NormalizedMask);
96 }
97
98 for (unsigned J = I + 1; J < E; ++J) {
99 ResourcePlusCycles &B = Worklist[J];
100 if ((NormalizedMask & B.first) == NormalizedMask) {
Andrea Di Biagio2008c7c2018-06-04 12:23:07 +0000101 B.second.CS.Subtract(A.second.size() - SuperResources[A.first]);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000102 if (countPopulation(B.first) > 1)
103 B.second.NumUnits++;
104 }
105 }
106 }
107
108 // A SchedWrite may specify a number of cycles in which a resource group
109 // is reserved. For example (on target x86; cpu Haswell):
110 //
111 // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
112 // let ResourceCycles = [2, 2, 3];
113 // }
114 //
115 // This means:
116 // Resource units HWPort0 and HWPort1 are both used for 2cy.
117 // Resource group HWPort01 is the union of HWPort0 and HWPort1.
118 // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
119 // will not be usable for 2 entire cycles from instruction issue.
120 //
121 // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
122 // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
123 // extra delay on top of the 2 cycles latency.
124 // During those extra cycles, HWPort01 is not usable by other instructions.
125 for (ResourcePlusCycles &RPC : ID.Resources) {
126 if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
127 // Remove the leading 1 from the resource group mask.
128 uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
129 if ((Mask & UsedResourceUnits) == Mask)
130 RPC.second.setReserved();
131 }
132 }
133
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000134 LLVM_DEBUG({
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000135 for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
136 dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n';
137 for (const uint64_t R : ID.Buffers)
138 dbgs() << "\t\tBuffer Mask=" << R << '\n';
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000139 });
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000140}
141
142static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
143 const MCSchedClassDesc &SCDesc,
144 const MCSubtargetInfo &STI) {
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000145 if (MCDesc.isCall()) {
146 // We cannot estimate how long this call will take.
147 // Artificially set an arbitrarily high latency (100cy).
Andrea Di Biagioc95a1302018-03-13 15:59:59 +0000148 ID.MaxLatency = 100U;
149 return;
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000150 }
151
Andrea Di Biagioc95a1302018-03-13 15:59:59 +0000152 int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
153 // If latency is unknown, then conservatively assume a MaxLatency of 100cy.
154 ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000155}
156
157static void populateWrites(InstrDesc &ID, const MCInst &MCI,
158 const MCInstrDesc &MCDesc,
159 const MCSchedClassDesc &SCDesc,
160 const MCSubtargetInfo &STI) {
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000161 // Set if writes through this opcode may update super registers.
162 // TODO: on x86-64, a 4 byte write of a general purpose register always
163 // fully updates the super-register.
164 // More in general, (at least on x86) not all register writes perform
165 // a partial (super-)register update.
166 // For example, an AVX instruction that writes on a XMM register implicitly
167 // zeroes the upper half of every aliasing super-register.
168 //
169 // For now, we pessimistically assume that writes are all potentially
170 // partial register updates. This is a good default for most targets, execept
171 // for those like x86 which implement a special semantic for certain opcodes.
172 // At least on x86, this may lead to an inaccurate prediction of the
173 // instruction level parallelism.
174 bool FullyUpdatesSuperRegisters = false;
175
176 // Now Populate Writes.
177
178 // This algorithm currently works under the strong (and potentially incorrect)
179 // assumption that information related to register def/uses can be obtained
180 // from MCInstrDesc.
181 //
182 // However class MCInstrDesc is used to describe MachineInstr objects and not
183 // MCInst objects. To be more specific, MCInstrDesc objects are opcode
184 // descriptors that are automatically generated via tablegen based on the
185 // instruction set information available from the target .td files. That
186 // means, the number of (explicit) definitions according to MCInstrDesc always
187 // matches the cardinality of the `(outs)` set in tablegen.
188 //
189 // By constructions, definitions must appear first in the operand sequence of
190 // a MachineInstr. Also, the (outs) sequence is preserved (example: the first
191 // element in the outs set is the first operand in the corresponding
192 // MachineInstr). That's the reason why MCInstrDesc only needs to declare the
193 // total number of register definitions, and not where those definitions are
194 // in the machine operand sequence.
195 //
196 // Unfortunately, it is not safe to use the information from MCInstrDesc to
197 // also describe MCInst objects. An MCInst object can be obtained from a
198 // MachineInstr through a lowering step which may restructure the operand
199 // sequence (and even remove or introduce new operands). So, there is a high
200 // risk that the lowering step breaks the assumptions that register
201 // definitions are always at the beginning of the machine operand sequence.
202 //
203 // This is a fundamental problem, and it is still an open problem. Essentially
204 // we have to find a way to correlate def/use operands of a MachineInstr to
205 // operands of an MCInst. Otherwise, we cannot correctly reconstruct data
206 // dependencies, nor we can correctly interpret the scheduling model, which
207 // heavily uses machine operand indices to define processor read-advance
208 // information, and to identify processor write resources. Essentially, we
209 // either need something like a MCInstrDesc, but for MCInst, or a way
210 // to map MCInst operands back to MachineInstr operands.
211 //
212 // Unfortunately, we don't have that information now. So, this prototype
213 // currently work under the strong assumption that we can always safely trust
214 // the content of an MCInstrDesc. For example, we can query a MCInstrDesc to
215 // obtain the number of explicit and implicit register defintions. We also
216 // assume that register definitions always come first in the operand sequence.
217 // This last assumption usually makes sense for MachineInstr, where register
218 // definitions always appear at the beginning of the operands sequence. In
219 // reality, these assumptions could be broken by the lowering step, which can
220 // decide to lay out operands in a different order than the original order of
221 // operand as specified by the MachineInstr.
222 //
223 // Things get even more complicated in the presence of "optional" register
224 // definitions. For MachineInstr, optional register definitions are always at
225 // the end of the operand sequence. Some ARM instructions that may update the
226 // status flags specify that register as a optional operand. Since we don't
227 // have operand descriptors for MCInst, we assume for now that the optional
228 // definition is always the last operand of a MCInst. Again, this assumption
229 // may be okay for most targets. However, there is no guarantee that targets
230 // would respect that.
231 //
232 // In conclusion: these are for now the strong assumptions made by the tool:
233 // * The number of explicit and implicit register definitions in a MCInst
234 // matches the number of explicit and implicit definitions according to
235 // the opcode descriptor (MCInstrDesc).
236 // * Register definitions take precedence over register uses in the operands
237 // list.
238 // * If an opcode specifies an optional definition, then the optional
239 // definition is always the last operand in the sequence, and it can be
240 // set to zero (i.e. "no register").
241 //
242 // These assumptions work quite well for most out-of-order in-tree targets
243 // like x86. This is mainly because the vast majority of instructions is
244 // expanded to MCInst using a straightforward lowering logic that preserves
245 // the ordering of the operands.
246 //
247 // In the longer term, we need to find a proper solution for this issue.
248 unsigned NumExplicitDefs = MCDesc.getNumDefs();
249 unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
250 unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
251 unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
252 if (MCDesc.hasOptionalDef())
253 TotalDefs++;
254 ID.Writes.resize(TotalDefs);
255 // Iterate over the operands list, and skip non-register operands.
256 // The first NumExplictDefs register operands are expected to be register
257 // definitions.
258 unsigned CurrentDef = 0;
259 unsigned i = 0;
260 for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
261 const MCOperand &Op = MCI.getOperand(i);
262 if (!Op.isReg())
263 continue;
264
265 WriteDescriptor &Write = ID.Writes[CurrentDef];
266 Write.OpIndex = i;
267 if (CurrentDef < NumWriteLatencyEntries) {
268 const MCWriteLatencyEntry &WLE =
269 *STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
270 // Conservatively default to MaxLatency.
271 Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles;
272 Write.SClassOrWriteResourceID = WLE.WriteResourceID;
273 } else {
274 // Assign a default latency for this write.
275 Write.Latency = ID.MaxLatency;
276 Write.SClassOrWriteResourceID = 0;
277 }
278 Write.FullyUpdatesSuperRegs = FullyUpdatesSuperRegisters;
279 Write.IsOptionalDef = false;
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000280 LLVM_DEBUG({
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000281 dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency
282 << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
283 });
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000284 CurrentDef++;
285 }
286
287 if (CurrentDef != NumExplicitDefs)
288 llvm::report_fatal_error(
289 "error: Expected more register operand definitions. ");
290
291 CurrentDef = 0;
292 for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
293 unsigned Index = NumExplicitDefs + CurrentDef;
294 WriteDescriptor &Write = ID.Writes[Index];
295 Write.OpIndex = -1;
296 Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
Andrea Di Biagio6fd62fe2018-04-02 13:46:49 +0000297 if (Index < NumWriteLatencyEntries) {
298 const MCWriteLatencyEntry &WLE =
299 *STI.getWriteLatencyEntry(&SCDesc, Index);
300 // Conservatively default to MaxLatency.
301 Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles;
302 Write.SClassOrWriteResourceID = WLE.WriteResourceID;
303 } else {
304 // Assign a default latency for this write.
305 Write.Latency = ID.MaxLatency;
306 Write.SClassOrWriteResourceID = 0;
307 }
308
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000309 Write.IsOptionalDef = false;
310 assert(Write.RegisterID != 0 && "Expected a valid phys register!");
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000311 LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg="
312 << Write.RegisterID << ", Latency=" << Write.Latency
313 << ", WriteResourceID=" << Write.SClassOrWriteResourceID
314 << '\n');
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000315 }
316
317 if (MCDesc.hasOptionalDef()) {
318 // Always assume that the optional definition is the last operand of the
319 // MCInst sequence.
320 const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1);
321 if (i == MCI.getNumOperands() || !Op.isReg())
322 llvm::report_fatal_error(
323 "error: expected a register operand for an optional "
324 "definition. Instruction has not be correctly analyzed.\n",
325 false);
326
327 WriteDescriptor &Write = ID.Writes[TotalDefs - 1];
328 Write.OpIndex = MCI.getNumOperands() - 1;
329 // Assign a default latency for this write.
330 Write.Latency = ID.MaxLatency;
331 Write.SClassOrWriteResourceID = 0;
332 Write.IsOptionalDef = true;
333 }
334}
335
336static void populateReads(InstrDesc &ID, const MCInst &MCI,
337 const MCInstrDesc &MCDesc,
338 const MCSchedClassDesc &SCDesc,
339 const MCSubtargetInfo &STI) {
340 unsigned SchedClassID = MCDesc.getSchedClass();
341 bool HasReadAdvanceEntries = SCDesc.NumReadAdvanceEntries > 0;
342
343 unsigned i = 0;
344 unsigned NumExplicitDefs = MCDesc.getNumDefs();
345 // Skip explicit definitions.
346 for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) {
347 const MCOperand &Op = MCI.getOperand(i);
348 if (Op.isReg())
349 NumExplicitDefs--;
350 }
351
352 if (NumExplicitDefs)
353 llvm::report_fatal_error(
354 "error: Expected more register operand definitions. ", false);
355
356 unsigned NumExplicitUses = MCI.getNumOperands() - i;
357 unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
358 if (MCDesc.hasOptionalDef()) {
359 assert(NumExplicitUses);
360 NumExplicitUses--;
361 }
362 unsigned TotalUses = NumExplicitUses + NumImplicitUses;
363 if (!TotalUses)
364 return;
365
366 ID.Reads.resize(TotalUses);
367 for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) {
368 ReadDescriptor &Read = ID.Reads[CurrentUse];
369 Read.OpIndex = i + CurrentUse;
Andrea Di Biagio0a837ef2018-03-29 14:26:56 +0000370 Read.UseIndex = CurrentUse;
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000371 Read.HasReadAdvanceEntries = HasReadAdvanceEntries;
372 Read.SchedClassID = SchedClassID;
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000373 LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndex);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000374 }
375
376 for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) {
377 ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse];
378 Read.OpIndex = -1;
Andrea Di Biagio6fd62fe2018-04-02 13:46:49 +0000379 Read.UseIndex = NumExplicitUses + CurrentUse;
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000380 Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse];
Andrea Di Biagio6fd62fe2018-04-02 13:46:49 +0000381 Read.HasReadAdvanceEntries = HasReadAdvanceEntries;
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000382 Read.SchedClassID = SchedClassID;
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000383 LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndex
384 << ", RegisterID=" << Read.RegisterID << '\n');
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000385 }
386}
387
Andrea Di Biagio49c85912018-05-04 13:10:10 +0000388const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000389 assert(STI.getSchedModel().hasInstrSchedModel() &&
390 "Itineraries are not yet supported!");
391
392 unsigned short Opcode = MCI.getOpcode();
393 // Obtain the instruction descriptor from the opcode.
394 const MCInstrDesc &MCDesc = MCII.get(Opcode);
395 const MCSchedModel &SM = STI.getSchedModel();
396
397 // Then obtain the scheduling class information from the instruction.
Andrea Di Biagio49c85912018-05-04 13:10:10 +0000398 unsigned SchedClassID = MCDesc.getSchedClass();
399 const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000400
401 // Create a new empty descriptor.
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000402 std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>();
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000403
404 if (SCDesc.isVariant()) {
Andrea Di Biagio24fb4fc2018-05-04 13:52:12 +0000405 WithColor::warning() << "don't know how to model variant opcodes.\n";
406 WithColor::note() << "assume 1 micro opcode.\n";
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000407 ID->NumMicroOps = 1U;
408 } else {
409 ID->NumMicroOps = SCDesc.NumMicroOps;
410 }
411
412 if (MCDesc.isCall()) {
413 // We don't correctly model calls.
Andrea Di Biagio24fb4fc2018-05-04 13:52:12 +0000414 WithColor::warning() << "found a call in the input assembly sequence.\n";
415 WithColor::note() << "call instructions are not correctly modeled. "
416 << "Assume a latency of 100cy.\n";
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000417 }
418
419 if (MCDesc.isReturn()) {
Andrea Di Biagio24fb4fc2018-05-04 13:52:12 +0000420 WithColor::warning() << "found a return instruction in the input"
421 << " assembly sequence.\n";
422 WithColor::note() << "program counter updates are ignored.\n";
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000423 }
424
425 ID->MayLoad = MCDesc.mayLoad();
426 ID->MayStore = MCDesc.mayStore();
427 ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects();
428
429 initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
Andrea Di Biagiodb66efc2018-04-25 09:38:58 +0000430 computeMaxLatency(*ID, MCDesc, SCDesc, STI);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000431 populateWrites(*ID, MCI, MCDesc, SCDesc, STI);
432 populateReads(*ID, MCI, MCDesc, SCDesc, STI);
433
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000434 LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
435 LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000436
437 // Now add the new descriptor.
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000438 Descriptors[Opcode] = std::move(ID);
Andrea Di Biagio49c85912018-05-04 13:10:10 +0000439 return *Descriptors[Opcode];
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000440}
441
Andrea Di Biagio4704f032018-03-20 12:25:54 +0000442const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
Andrea Di Biagio35622482018-03-22 10:19:20 +0000443 if (Descriptors.find_as(MCI.getOpcode()) == Descriptors.end())
Andrea Di Biagio49c85912018-05-04 13:10:10 +0000444 return createInstrDescImpl(MCI);
Andrea Di Biagio35622482018-03-22 10:19:20 +0000445 return *Descriptors[MCI.getOpcode()];
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000446}
447
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000448std::unique_ptr<Instruction>
Andrea Di Biagio49c85912018-05-04 13:10:10 +0000449InstrBuilder::createInstruction(const MCInst &MCI) {
Andrea Di Biagio4704f032018-03-20 12:25:54 +0000450 const InstrDesc &D = getOrCreateInstrDesc(MCI);
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000451 std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000452
Andrea Di Biagiodb66efc2018-04-25 09:38:58 +0000453 // Initialize Reads first.
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000454 for (const ReadDescriptor &RD : D.Reads) {
455 int RegID = -1;
456 if (RD.OpIndex != -1) {
457 // explicit read.
458 const MCOperand &Op = MCI.getOperand(RD.OpIndex);
459 // Skip non-register operands.
460 if (!Op.isReg())
461 continue;
462 RegID = Op.getReg();
463 } else {
464 // Implicit read.
465 RegID = RD.RegisterID;
466 }
467
468 // Skip invalid register operands.
469 if (!RegID)
470 continue;
471
472 // Okay, this is a register operand. Create a ReadState for it.
473 assert(RegID > 0 && "Invalid register ID found!");
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000474 NewIS->getUses().emplace_back(llvm::make_unique<ReadState>(RD, RegID));
Andrea Di Biagio4704f032018-03-20 12:25:54 +0000475 }
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000476
Andrea Di Biagiodb66efc2018-04-25 09:38:58 +0000477 // Initialize writes.
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000478 for (const WriteDescriptor &WD : D.Writes) {
479 unsigned RegID =
480 WD.OpIndex == -1 ? WD.RegisterID : MCI.getOperand(WD.OpIndex).getReg();
Andrea Di Biagio35622482018-03-22 10:19:20 +0000481 // Check if this is a optional definition that references NoReg.
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000482 if (WD.IsOptionalDef && !RegID)
483 continue;
484
Andrea Di Biagio35622482018-03-22 10:19:20 +0000485 assert(RegID && "Expected a valid register ID!");
Andrea Di Biagio7b3d1622018-03-20 12:58:34 +0000486 NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(WD, RegID));
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000487 }
488
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000489 return NewIS;
490}
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000491} // namespace mca