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