xref: /freebsd/contrib/llvm-project/llvm/lib/MCA/InstrBuilder.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
10b57cec5SDimitry Andric //===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric /// \file
90b57cec5SDimitry Andric ///
100b57cec5SDimitry Andric /// This file implements the InstrBuilder interface.
110b57cec5SDimitry Andric ///
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/MCA/InstrBuilder.h"
150b57cec5SDimitry Andric #include "llvm/ADT/APInt.h"
160b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
17*81ad6265SDimitry Andric #include "llvm/ADT/Statistic.h"
180b57cec5SDimitry Andric #include "llvm/MC/MCInst.h"
190b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
200b57cec5SDimitry Andric #include "llvm/Support/WithColor.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
220b57cec5SDimitry Andric 
23*81ad6265SDimitry Andric #define DEBUG_TYPE "llvm-mca-instrbuilder"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric namespace llvm {
260b57cec5SDimitry Andric namespace mca {
270b57cec5SDimitry Andric 
28*81ad6265SDimitry Andric char RecycledInstErr::ID = 0;
29*81ad6265SDimitry Andric 
300b57cec5SDimitry Andric InstrBuilder::InstrBuilder(const llvm::MCSubtargetInfo &sti,
310b57cec5SDimitry Andric                            const llvm::MCInstrInfo &mcii,
320b57cec5SDimitry Andric                            const llvm::MCRegisterInfo &mri,
330b57cec5SDimitry Andric                            const llvm::MCInstrAnalysis *mcia)
340b57cec5SDimitry Andric     : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia), FirstCallInst(true),
350b57cec5SDimitry Andric       FirstReturnInst(true) {
360b57cec5SDimitry Andric   const MCSchedModel &SM = STI.getSchedModel();
370b57cec5SDimitry Andric   ProcResourceMasks.resize(SM.getNumProcResourceKinds());
380b57cec5SDimitry Andric   computeProcResourceMasks(STI.getSchedModel(), ProcResourceMasks);
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric static void initializeUsedResources(InstrDesc &ID,
420b57cec5SDimitry Andric                                     const MCSchedClassDesc &SCDesc,
430b57cec5SDimitry Andric                                     const MCSubtargetInfo &STI,
440b57cec5SDimitry Andric                                     ArrayRef<uint64_t> ProcResourceMasks) {
450b57cec5SDimitry Andric   const MCSchedModel &SM = STI.getSchedModel();
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   // Populate resources consumed.
480b57cec5SDimitry Andric   using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
49fe6060f1SDimitry Andric   SmallVector<ResourcePlusCycles, 4> Worklist;
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric   // Track cycles contributed by resources that are in a "Super" relationship.
520b57cec5SDimitry Andric   // This is required if we want to correctly match the behavior of method
530b57cec5SDimitry Andric   // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
540b57cec5SDimitry Andric   // of "consumed" processor resources and resource cycles, the logic in
550b57cec5SDimitry Andric   // ExpandProcResource() doesn't update the number of resource cycles
560b57cec5SDimitry Andric   // contributed by a "Super" resource to a group.
570b57cec5SDimitry Andric   // We need to take this into account when we find that a processor resource is
580b57cec5SDimitry Andric   // part of a group, and it is also used as the "Super" of other resources.
590b57cec5SDimitry Andric   // This map stores the number of cycles contributed by sub-resources that are
600b57cec5SDimitry Andric   // part of a "Super" resource. The key value is the "Super" resource mask ID.
610b57cec5SDimitry Andric   DenseMap<uint64_t, unsigned> SuperResources;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   unsigned NumProcResources = SM.getNumProcResourceKinds();
640b57cec5SDimitry Andric   APInt Buffers(NumProcResources, 0);
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   bool AllInOrderResources = true;
670b57cec5SDimitry Andric   bool AnyDispatchHazards = false;
680b57cec5SDimitry Andric   for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
690b57cec5SDimitry Andric     const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
700b57cec5SDimitry Andric     const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
710b57cec5SDimitry Andric     if (!PRE->Cycles) {
720b57cec5SDimitry Andric #ifndef NDEBUG
730b57cec5SDimitry Andric       WithColor::warning()
740b57cec5SDimitry Andric           << "Ignoring invalid write of zero cycles on processor resource "
750b57cec5SDimitry Andric           << PR.Name << "\n";
760b57cec5SDimitry Andric       WithColor::note() << "found in scheduling class " << SCDesc.Name
770b57cec5SDimitry Andric                         << " (write index #" << I << ")\n";
780b57cec5SDimitry Andric #endif
790b57cec5SDimitry Andric       continue;
800b57cec5SDimitry Andric     }
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric     uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
830b57cec5SDimitry Andric     if (PR.BufferSize < 0) {
840b57cec5SDimitry Andric       AllInOrderResources = false;
850b57cec5SDimitry Andric     } else {
868bcb0991SDimitry Andric       Buffers.setBit(getResourceStateIndex(Mask));
870b57cec5SDimitry Andric       AnyDispatchHazards |= (PR.BufferSize == 0);
880b57cec5SDimitry Andric       AllInOrderResources &= (PR.BufferSize <= 1);
890b57cec5SDimitry Andric     }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric     CycleSegment RCy(0, PRE->Cycles, false);
920b57cec5SDimitry Andric     Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
930b57cec5SDimitry Andric     if (PR.SuperIdx) {
940b57cec5SDimitry Andric       uint64_t Super = ProcResourceMasks[PR.SuperIdx];
950b57cec5SDimitry Andric       SuperResources[Super] += PRE->Cycles;
960b57cec5SDimitry Andric     }
970b57cec5SDimitry Andric   }
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric   ID.MustIssueImmediately = AllInOrderResources && AnyDispatchHazards;
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   // Sort elements by mask popcount, so that we prioritize resource units over
1020b57cec5SDimitry Andric   // resource groups, and smaller groups over larger groups.
1030b57cec5SDimitry Andric   sort(Worklist, [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
1040b57cec5SDimitry Andric     unsigned popcntA = countPopulation(A.first);
1050b57cec5SDimitry Andric     unsigned popcntB = countPopulation(B.first);
1060b57cec5SDimitry Andric     if (popcntA < popcntB)
1070b57cec5SDimitry Andric       return true;
1080b57cec5SDimitry Andric     if (popcntA > popcntB)
1090b57cec5SDimitry Andric       return false;
1100b57cec5SDimitry Andric     return A.first < B.first;
1110b57cec5SDimitry Andric   });
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   uint64_t UsedResourceUnits = 0;
1140b57cec5SDimitry Andric   uint64_t UsedResourceGroups = 0;
115fe6060f1SDimitry Andric   auto GroupIt = find_if(Worklist, [](const ResourcePlusCycles &Elt) {
116fe6060f1SDimitry Andric     return countPopulation(Elt.first) > 1;
117fe6060f1SDimitry Andric   });
118fe6060f1SDimitry Andric   unsigned FirstGroupIdx = std::distance(Worklist.begin(), GroupIt);
119fe6060f1SDimitry Andric   uint64_t ImpliedUsesOfResourceUnits = 0;
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   // Remove cycles contributed by smaller resources.
1220b57cec5SDimitry Andric   for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
1230b57cec5SDimitry Andric     ResourcePlusCycles &A = Worklist[I];
1240b57cec5SDimitry Andric     if (!A.second.size()) {
1250b57cec5SDimitry Andric       assert(countPopulation(A.first) > 1 && "Expected a group!");
1260b57cec5SDimitry Andric       UsedResourceGroups |= PowerOf2Floor(A.first);
1270b57cec5SDimitry Andric       continue;
1280b57cec5SDimitry Andric     }
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric     ID.Resources.emplace_back(A);
1310b57cec5SDimitry Andric     uint64_t NormalizedMask = A.first;
1320b57cec5SDimitry Andric     if (countPopulation(A.first) == 1) {
1330b57cec5SDimitry Andric       UsedResourceUnits |= A.first;
1340b57cec5SDimitry Andric     } else {
1350b57cec5SDimitry Andric       // Remove the leading 1 from the resource group mask.
1360b57cec5SDimitry Andric       NormalizedMask ^= PowerOf2Floor(NormalizedMask);
1370b57cec5SDimitry Andric       UsedResourceGroups |= (A.first ^ NormalizedMask);
138fe6060f1SDimitry Andric 
139fe6060f1SDimitry Andric       uint64_t AvailableMask = NormalizedMask & ~UsedResourceUnits;
140fe6060f1SDimitry Andric       if ((NormalizedMask != AvailableMask) &&
141fe6060f1SDimitry Andric           countPopulation(AvailableMask) == 1) {
142fe6060f1SDimitry Andric         // At simulation time, this resource group use will decay into a simple
143fe6060f1SDimitry Andric         // use of the resource unit identified by `AvailableMask`.
144fe6060f1SDimitry Andric         ImpliedUsesOfResourceUnits |= AvailableMask;
145fe6060f1SDimitry Andric         UsedResourceUnits |= AvailableMask;
146fe6060f1SDimitry Andric       }
1470b57cec5SDimitry Andric     }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric     for (unsigned J = I + 1; J < E; ++J) {
1500b57cec5SDimitry Andric       ResourcePlusCycles &B = Worklist[J];
1510b57cec5SDimitry Andric       if ((NormalizedMask & B.first) == NormalizedMask) {
1520b57cec5SDimitry Andric         B.second.CS.subtract(A.second.size() - SuperResources[A.first]);
1530b57cec5SDimitry Andric         if (countPopulation(B.first) > 1)
1540b57cec5SDimitry Andric           B.second.NumUnits++;
1550b57cec5SDimitry Andric       }
1560b57cec5SDimitry Andric     }
1570b57cec5SDimitry Andric   }
1580b57cec5SDimitry Andric 
159fe6060f1SDimitry Andric   // Look for implicit uses of processor resource units. These are resource
160fe6060f1SDimitry Andric   // units which are indirectly consumed by resource groups, and that must be
161fe6060f1SDimitry Andric   // always available on instruction issue.
162fe6060f1SDimitry Andric   while (ImpliedUsesOfResourceUnits) {
163fe6060f1SDimitry Andric     ID.ImplicitlyUsedProcResUnits |= ImpliedUsesOfResourceUnits;
164fe6060f1SDimitry Andric     ImpliedUsesOfResourceUnits = 0;
165fe6060f1SDimitry Andric     for (unsigned I = FirstGroupIdx, E = Worklist.size(); I < E; ++I) {
166fe6060f1SDimitry Andric       ResourcePlusCycles &A = Worklist[I];
167fe6060f1SDimitry Andric       if (!A.second.size())
168fe6060f1SDimitry Andric         continue;
169fe6060f1SDimitry Andric 
170fe6060f1SDimitry Andric       uint64_t NormalizedMask = A.first;
171fe6060f1SDimitry Andric       assert(countPopulation(NormalizedMask) > 1);
172fe6060f1SDimitry Andric       // Remove the leading 1 from the resource group mask.
173fe6060f1SDimitry Andric       NormalizedMask ^= PowerOf2Floor(NormalizedMask);
174fe6060f1SDimitry Andric       uint64_t AvailableMask = NormalizedMask & ~UsedResourceUnits;
175fe6060f1SDimitry Andric       if ((NormalizedMask != AvailableMask) &&
176fe6060f1SDimitry Andric           countPopulation(AvailableMask) != 1)
177fe6060f1SDimitry Andric         continue;
178fe6060f1SDimitry Andric 
179fe6060f1SDimitry Andric       UsedResourceUnits |= AvailableMask;
180fe6060f1SDimitry Andric       ImpliedUsesOfResourceUnits |= AvailableMask;
181fe6060f1SDimitry Andric     }
182fe6060f1SDimitry Andric   }
183fe6060f1SDimitry Andric 
1840b57cec5SDimitry Andric   // A SchedWrite may specify a number of cycles in which a resource group
1850b57cec5SDimitry Andric   // is reserved. For example (on target x86; cpu Haswell):
1860b57cec5SDimitry Andric   //
1870b57cec5SDimitry Andric   //  SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
1880b57cec5SDimitry Andric   //    let ResourceCycles = [2, 2, 3];
1890b57cec5SDimitry Andric   //  }
1900b57cec5SDimitry Andric   //
1910b57cec5SDimitry Andric   // This means:
1920b57cec5SDimitry Andric   // Resource units HWPort0 and HWPort1 are both used for 2cy.
1930b57cec5SDimitry Andric   // Resource group HWPort01 is the union of HWPort0 and HWPort1.
1940b57cec5SDimitry Andric   // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
1950b57cec5SDimitry Andric   // will not be usable for 2 entire cycles from instruction issue.
1960b57cec5SDimitry Andric   //
1970b57cec5SDimitry Andric   // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
1980b57cec5SDimitry Andric   // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
1990b57cec5SDimitry Andric   // extra delay on top of the 2 cycles latency.
2000b57cec5SDimitry Andric   // During those extra cycles, HWPort01 is not usable by other instructions.
2010b57cec5SDimitry Andric   for (ResourcePlusCycles &RPC : ID.Resources) {
2020b57cec5SDimitry Andric     if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
2030b57cec5SDimitry Andric       // Remove the leading 1 from the resource group mask.
2040b57cec5SDimitry Andric       uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
2055ffd83dbSDimitry Andric       uint64_t MaxResourceUnits = countPopulation(Mask);
2065ffd83dbSDimitry Andric       if (RPC.second.NumUnits > countPopulation(Mask)) {
2070b57cec5SDimitry Andric         RPC.second.setReserved();
2085ffd83dbSDimitry Andric         RPC.second.NumUnits = MaxResourceUnits;
2095ffd83dbSDimitry Andric       }
2100b57cec5SDimitry Andric     }
2110b57cec5SDimitry Andric   }
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric   // Identify extra buffers that are consumed through super resources.
2140b57cec5SDimitry Andric   for (const std::pair<uint64_t, unsigned> &SR : SuperResources) {
2150b57cec5SDimitry Andric     for (unsigned I = 1, E = NumProcResources; I < E; ++I) {
2160b57cec5SDimitry Andric       const MCProcResourceDesc &PR = *SM.getProcResource(I);
2170b57cec5SDimitry Andric       if (PR.BufferSize == -1)
2180b57cec5SDimitry Andric         continue;
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric       uint64_t Mask = ProcResourceMasks[I];
2210b57cec5SDimitry Andric       if (Mask != SR.first && ((Mask & SR.first) == SR.first))
2228bcb0991SDimitry Andric         Buffers.setBit(getResourceStateIndex(Mask));
2230b57cec5SDimitry Andric     }
2240b57cec5SDimitry Andric   }
2250b57cec5SDimitry Andric 
2268bcb0991SDimitry Andric   ID.UsedBuffers = Buffers.getZExtValue();
2278bcb0991SDimitry Andric   ID.UsedProcResUnits = UsedResourceUnits;
2288bcb0991SDimitry Andric   ID.UsedProcResGroups = UsedResourceGroups;
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric   LLVM_DEBUG({
2310b57cec5SDimitry Andric     for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
2320b57cec5SDimitry Andric       dbgs() << "\t\tResource Mask=" << format_hex(R.first, 16) << ", "
2330b57cec5SDimitry Andric              << "Reserved=" << R.second.isReserved() << ", "
2340b57cec5SDimitry Andric              << "#Units=" << R.second.NumUnits << ", "
2350b57cec5SDimitry Andric              << "cy=" << R.second.size() << '\n';
2368bcb0991SDimitry Andric     uint64_t BufferIDs = ID.UsedBuffers;
2378bcb0991SDimitry Andric     while (BufferIDs) {
2388bcb0991SDimitry Andric       uint64_t Current = BufferIDs & (-BufferIDs);
2398bcb0991SDimitry Andric       dbgs() << "\t\tBuffer Mask=" << format_hex(Current, 16) << '\n';
2408bcb0991SDimitry Andric       BufferIDs ^= Current;
2418bcb0991SDimitry Andric     }
2420b57cec5SDimitry Andric     dbgs() << "\t\t Used Units=" << format_hex(ID.UsedProcResUnits, 16) << '\n';
243fe6060f1SDimitry Andric     dbgs() << "\t\tImplicitly Used Units="
244fe6060f1SDimitry Andric            << format_hex(ID.ImplicitlyUsedProcResUnits, 16) << '\n';
2450b57cec5SDimitry Andric     dbgs() << "\t\tUsed Groups=" << format_hex(ID.UsedProcResGroups, 16)
2460b57cec5SDimitry Andric            << '\n';
2470b57cec5SDimitry Andric   });
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric 
2500b57cec5SDimitry Andric static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
2510b57cec5SDimitry Andric                               const MCSchedClassDesc &SCDesc,
2520b57cec5SDimitry Andric                               const MCSubtargetInfo &STI) {
2530b57cec5SDimitry Andric   if (MCDesc.isCall()) {
2540b57cec5SDimitry Andric     // We cannot estimate how long this call will take.
2550b57cec5SDimitry Andric     // Artificially set an arbitrarily high latency (100cy).
2560b57cec5SDimitry Andric     ID.MaxLatency = 100U;
2570b57cec5SDimitry Andric     return;
2580b57cec5SDimitry Andric   }
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric   int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
2610b57cec5SDimitry Andric   // If latency is unknown, then conservatively assume a MaxLatency of 100cy.
2620b57cec5SDimitry Andric   ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
2630b57cec5SDimitry Andric }
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric static Error verifyOperands(const MCInstrDesc &MCDesc, const MCInst &MCI) {
2660b57cec5SDimitry Andric   // Count register definitions, and skip non register operands in the process.
2670b57cec5SDimitry Andric   unsigned I, E;
2680b57cec5SDimitry Andric   unsigned NumExplicitDefs = MCDesc.getNumDefs();
2690b57cec5SDimitry Andric   for (I = 0, E = MCI.getNumOperands(); NumExplicitDefs && I < E; ++I) {
2700b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(I);
2710b57cec5SDimitry Andric     if (Op.isReg())
2720b57cec5SDimitry Andric       --NumExplicitDefs;
2730b57cec5SDimitry Andric   }
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric   if (NumExplicitDefs) {
2760b57cec5SDimitry Andric     return make_error<InstructionError<MCInst>>(
2770b57cec5SDimitry Andric         "Expected more register operand definitions.", MCI);
2780b57cec5SDimitry Andric   }
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   if (MCDesc.hasOptionalDef()) {
2810b57cec5SDimitry Andric     // Always assume that the optional definition is the last operand.
2820b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(MCDesc.getNumOperands() - 1);
2830b57cec5SDimitry Andric     if (I == MCI.getNumOperands() || !Op.isReg()) {
2840b57cec5SDimitry Andric       std::string Message =
2850b57cec5SDimitry Andric           "expected a register operand for an optional definition. Instruction "
2860b57cec5SDimitry Andric           "has not been correctly analyzed.";
2870b57cec5SDimitry Andric       return make_error<InstructionError<MCInst>>(Message, MCI);
2880b57cec5SDimitry Andric     }
2890b57cec5SDimitry Andric   }
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric   return ErrorSuccess();
2920b57cec5SDimitry Andric }
2930b57cec5SDimitry Andric 
2940b57cec5SDimitry Andric void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
2950b57cec5SDimitry Andric                                   unsigned SchedClassID) {
2960b57cec5SDimitry Andric   const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
2970b57cec5SDimitry Andric   const MCSchedModel &SM = STI.getSchedModel();
2980b57cec5SDimitry Andric   const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric   // Assumptions made by this algorithm:
3010b57cec5SDimitry Andric   //  1. The number of explicit and implicit register definitions in a MCInst
3020b57cec5SDimitry Andric   //     matches the number of explicit and implicit definitions according to
3030b57cec5SDimitry Andric   //     the opcode descriptor (MCInstrDesc).
3040b57cec5SDimitry Andric   //  2. Uses start at index #(MCDesc.getNumDefs()).
3050b57cec5SDimitry Andric   //  3. There can only be a single optional register definition, an it is
306e8d8bef9SDimitry Andric   //     either the last operand of the sequence (excluding extra operands
307e8d8bef9SDimitry Andric   //     contributed by variadic opcodes) or one of the explicit register
308e8d8bef9SDimitry Andric   //     definitions. The latter occurs for some Thumb1 instructions.
3090b57cec5SDimitry Andric   //
3100b57cec5SDimitry Andric   // These assumptions work quite well for most out-of-order in-tree targets
3110b57cec5SDimitry Andric   // like x86. This is mainly because the vast majority of instructions is
3120b57cec5SDimitry Andric   // expanded to MCInst using a straightforward lowering logic that preserves
3130b57cec5SDimitry Andric   // the ordering of the operands.
3140b57cec5SDimitry Andric   //
3150b57cec5SDimitry Andric   // About assumption 1.
3160b57cec5SDimitry Andric   // The algorithm allows non-register operands between register operand
3170b57cec5SDimitry Andric   // definitions. This helps to handle some special ARM instructions with
3180b57cec5SDimitry Andric   // implicit operand increment (-mtriple=armv7):
3190b57cec5SDimitry Andric   //
3200b57cec5SDimitry Andric   // vld1.32  {d18, d19}, [r1]!  @ <MCInst #1463 VLD1q32wb_fixed
3210b57cec5SDimitry Andric   //                             @  <MCOperand Reg:59>
3220b57cec5SDimitry Andric   //                             @  <MCOperand Imm:0>     (!!)
3230b57cec5SDimitry Andric   //                             @  <MCOperand Reg:67>
3240b57cec5SDimitry Andric   //                             @  <MCOperand Imm:0>
3250b57cec5SDimitry Andric   //                             @  <MCOperand Imm:14>
3260b57cec5SDimitry Andric   //                             @  <MCOperand Reg:0>>
3270b57cec5SDimitry Andric   //
3280b57cec5SDimitry Andric   // MCDesc reports:
3290b57cec5SDimitry Andric   //  6 explicit operands.
3300b57cec5SDimitry Andric   //  1 optional definition
3310b57cec5SDimitry Andric   //  2 explicit definitions (!!)
3320b57cec5SDimitry Andric   //
3330b57cec5SDimitry Andric   // The presence of an 'Imm' operand between the two register definitions
3340b57cec5SDimitry Andric   // breaks the assumption that "register definitions are always at the
3350b57cec5SDimitry Andric   // beginning of the operand sequence".
3360b57cec5SDimitry Andric   //
3370b57cec5SDimitry Andric   // To workaround this issue, this algorithm ignores (i.e. skips) any
3380b57cec5SDimitry Andric   // non-register operands between register definitions.  The optional
3390b57cec5SDimitry Andric   // definition is still at index #(NumOperands-1).
3400b57cec5SDimitry Andric   //
3410b57cec5SDimitry Andric   // According to assumption 2. register reads start at #(NumExplicitDefs-1).
3420b57cec5SDimitry Andric   // That means, register R1 from the example is both read and written.
3430b57cec5SDimitry Andric   unsigned NumExplicitDefs = MCDesc.getNumDefs();
3440b57cec5SDimitry Andric   unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
3450b57cec5SDimitry Andric   unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
3460b57cec5SDimitry Andric   unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
3470b57cec5SDimitry Andric   if (MCDesc.hasOptionalDef())
3480b57cec5SDimitry Andric     TotalDefs++;
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands();
3510b57cec5SDimitry Andric   ID.Writes.resize(TotalDefs + NumVariadicOps);
3520b57cec5SDimitry Andric   // Iterate over the operands list, and skip non-register operands.
353480093f4SDimitry Andric   // The first NumExplicitDefs register operands are expected to be register
3540b57cec5SDimitry Andric   // definitions.
3550b57cec5SDimitry Andric   unsigned CurrentDef = 0;
356e8d8bef9SDimitry Andric   unsigned OptionalDefIdx = MCDesc.getNumOperands() - 1;
3570b57cec5SDimitry Andric   unsigned i = 0;
3580b57cec5SDimitry Andric   for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
3590b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(i);
3600b57cec5SDimitry Andric     if (!Op.isReg())
3610b57cec5SDimitry Andric       continue;
3620b57cec5SDimitry Andric 
363e8d8bef9SDimitry Andric     if (MCDesc.OpInfo[CurrentDef].isOptionalDef()) {
364e8d8bef9SDimitry Andric       OptionalDefIdx = CurrentDef++;
365e8d8bef9SDimitry Andric       continue;
366e8d8bef9SDimitry Andric     }
367e8d8bef9SDimitry Andric 
3680b57cec5SDimitry Andric     WriteDescriptor &Write = ID.Writes[CurrentDef];
3690b57cec5SDimitry Andric     Write.OpIndex = i;
3700b57cec5SDimitry Andric     if (CurrentDef < NumWriteLatencyEntries) {
3710b57cec5SDimitry Andric       const MCWriteLatencyEntry &WLE =
3720b57cec5SDimitry Andric           *STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
3730b57cec5SDimitry Andric       // Conservatively default to MaxLatency.
3740b57cec5SDimitry Andric       Write.Latency =
3750b57cec5SDimitry Andric           WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
3760b57cec5SDimitry Andric       Write.SClassOrWriteResourceID = WLE.WriteResourceID;
3770b57cec5SDimitry Andric     } else {
3780b57cec5SDimitry Andric       // Assign a default latency for this write.
3790b57cec5SDimitry Andric       Write.Latency = ID.MaxLatency;
3800b57cec5SDimitry Andric       Write.SClassOrWriteResourceID = 0;
3810b57cec5SDimitry Andric     }
3820b57cec5SDimitry Andric     Write.IsOptionalDef = false;
3830b57cec5SDimitry Andric     LLVM_DEBUG({
3840b57cec5SDimitry Andric       dbgs() << "\t\t[Def]    OpIdx=" << Write.OpIndex
3850b57cec5SDimitry Andric              << ", Latency=" << Write.Latency
3860b57cec5SDimitry Andric              << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
3870b57cec5SDimitry Andric     });
3880b57cec5SDimitry Andric     CurrentDef++;
3890b57cec5SDimitry Andric   }
3900b57cec5SDimitry Andric 
3910b57cec5SDimitry Andric   assert(CurrentDef == NumExplicitDefs &&
3920b57cec5SDimitry Andric          "Expected more register operand definitions.");
3930b57cec5SDimitry Andric   for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
3940b57cec5SDimitry Andric     unsigned Index = NumExplicitDefs + CurrentDef;
3950b57cec5SDimitry Andric     WriteDescriptor &Write = ID.Writes[Index];
3960b57cec5SDimitry Andric     Write.OpIndex = ~CurrentDef;
3970b57cec5SDimitry Andric     Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
3980b57cec5SDimitry Andric     if (Index < NumWriteLatencyEntries) {
3990b57cec5SDimitry Andric       const MCWriteLatencyEntry &WLE =
4000b57cec5SDimitry Andric           *STI.getWriteLatencyEntry(&SCDesc, Index);
4010b57cec5SDimitry Andric       // Conservatively default to MaxLatency.
4020b57cec5SDimitry Andric       Write.Latency =
4030b57cec5SDimitry Andric           WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
4040b57cec5SDimitry Andric       Write.SClassOrWriteResourceID = WLE.WriteResourceID;
4050b57cec5SDimitry Andric     } else {
4060b57cec5SDimitry Andric       // Assign a default latency for this write.
4070b57cec5SDimitry Andric       Write.Latency = ID.MaxLatency;
4080b57cec5SDimitry Andric       Write.SClassOrWriteResourceID = 0;
4090b57cec5SDimitry Andric     }
4100b57cec5SDimitry Andric 
4110b57cec5SDimitry Andric     Write.IsOptionalDef = false;
4120b57cec5SDimitry Andric     assert(Write.RegisterID != 0 && "Expected a valid phys register!");
4130b57cec5SDimitry Andric     LLVM_DEBUG({
4140b57cec5SDimitry Andric       dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex
4150b57cec5SDimitry Andric              << ", PhysReg=" << MRI.getName(Write.RegisterID)
4160b57cec5SDimitry Andric              << ", Latency=" << Write.Latency
4170b57cec5SDimitry Andric              << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
4180b57cec5SDimitry Andric     });
4190b57cec5SDimitry Andric   }
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric   if (MCDesc.hasOptionalDef()) {
4220b57cec5SDimitry Andric     WriteDescriptor &Write = ID.Writes[NumExplicitDefs + NumImplicitDefs];
423e8d8bef9SDimitry Andric     Write.OpIndex = OptionalDefIdx;
4240b57cec5SDimitry Andric     // Assign a default latency for this write.
4250b57cec5SDimitry Andric     Write.Latency = ID.MaxLatency;
4260b57cec5SDimitry Andric     Write.SClassOrWriteResourceID = 0;
4270b57cec5SDimitry Andric     Write.IsOptionalDef = true;
4280b57cec5SDimitry Andric     LLVM_DEBUG({
4290b57cec5SDimitry Andric       dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex
4300b57cec5SDimitry Andric              << ", Latency=" << Write.Latency
4310b57cec5SDimitry Andric              << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
4320b57cec5SDimitry Andric     });
4330b57cec5SDimitry Andric   }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric   if (!NumVariadicOps)
4360b57cec5SDimitry Andric     return;
4370b57cec5SDimitry Andric 
438fe6060f1SDimitry Andric   bool AssumeUsesOnly = !MCDesc.variadicOpsAreDefs();
4390b57cec5SDimitry Andric   CurrentDef = NumExplicitDefs + NumImplicitDefs + MCDesc.hasOptionalDef();
4400b57cec5SDimitry Andric   for (unsigned I = 0, OpIndex = MCDesc.getNumOperands();
4410b57cec5SDimitry Andric        I < NumVariadicOps && !AssumeUsesOnly; ++I, ++OpIndex) {
4420b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(OpIndex);
4430b57cec5SDimitry Andric     if (!Op.isReg())
4440b57cec5SDimitry Andric       continue;
4450b57cec5SDimitry Andric 
4460b57cec5SDimitry Andric     WriteDescriptor &Write = ID.Writes[CurrentDef];
4470b57cec5SDimitry Andric     Write.OpIndex = OpIndex;
4480b57cec5SDimitry Andric     // Assign a default latency for this write.
4490b57cec5SDimitry Andric     Write.Latency = ID.MaxLatency;
4500b57cec5SDimitry Andric     Write.SClassOrWriteResourceID = 0;
4510b57cec5SDimitry Andric     Write.IsOptionalDef = false;
4520b57cec5SDimitry Andric     ++CurrentDef;
4530b57cec5SDimitry Andric     LLVM_DEBUG({
4540b57cec5SDimitry Andric       dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex
4550b57cec5SDimitry Andric              << ", Latency=" << Write.Latency
4560b57cec5SDimitry Andric              << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
4570b57cec5SDimitry Andric     });
4580b57cec5SDimitry Andric   }
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric   ID.Writes.resize(CurrentDef);
4610b57cec5SDimitry Andric }
4620b57cec5SDimitry Andric 
4630b57cec5SDimitry Andric void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
4640b57cec5SDimitry Andric                                  unsigned SchedClassID) {
4650b57cec5SDimitry Andric   const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
4660b57cec5SDimitry Andric   unsigned NumExplicitUses = MCDesc.getNumOperands() - MCDesc.getNumDefs();
4670b57cec5SDimitry Andric   unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
4680b57cec5SDimitry Andric   // Remove the optional definition.
4690b57cec5SDimitry Andric   if (MCDesc.hasOptionalDef())
4700b57cec5SDimitry Andric     --NumExplicitUses;
4710b57cec5SDimitry Andric   unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands();
4720b57cec5SDimitry Andric   unsigned TotalUses = NumExplicitUses + NumImplicitUses + NumVariadicOps;
4730b57cec5SDimitry Andric   ID.Reads.resize(TotalUses);
4740b57cec5SDimitry Andric   unsigned CurrentUse = 0;
4750b57cec5SDimitry Andric   for (unsigned I = 0, OpIndex = MCDesc.getNumDefs(); I < NumExplicitUses;
4760b57cec5SDimitry Andric        ++I, ++OpIndex) {
4770b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(OpIndex);
4780b57cec5SDimitry Andric     if (!Op.isReg())
4790b57cec5SDimitry Andric       continue;
4800b57cec5SDimitry Andric 
4810b57cec5SDimitry Andric     ReadDescriptor &Read = ID.Reads[CurrentUse];
4820b57cec5SDimitry Andric     Read.OpIndex = OpIndex;
4830b57cec5SDimitry Andric     Read.UseIndex = I;
4840b57cec5SDimitry Andric     Read.SchedClassID = SchedClassID;
4850b57cec5SDimitry Andric     ++CurrentUse;
4860b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\t\t[Use]    OpIdx=" << Read.OpIndex
4870b57cec5SDimitry Andric                       << ", UseIndex=" << Read.UseIndex << '\n');
4880b57cec5SDimitry Andric   }
4890b57cec5SDimitry Andric 
4900b57cec5SDimitry Andric   // For the purpose of ReadAdvance, implicit uses come directly after explicit
4910b57cec5SDimitry Andric   // uses. The "UseIndex" must be updated according to that implicit layout.
4920b57cec5SDimitry Andric   for (unsigned I = 0; I < NumImplicitUses; ++I) {
4930b57cec5SDimitry Andric     ReadDescriptor &Read = ID.Reads[CurrentUse + I];
4940b57cec5SDimitry Andric     Read.OpIndex = ~I;
4950b57cec5SDimitry Andric     Read.UseIndex = NumExplicitUses + I;
4960b57cec5SDimitry Andric     Read.RegisterID = MCDesc.getImplicitUses()[I];
4970b57cec5SDimitry Andric     Read.SchedClassID = SchedClassID;
4980b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndex
4990b57cec5SDimitry Andric                       << ", UseIndex=" << Read.UseIndex << ", RegisterID="
5000b57cec5SDimitry Andric                       << MRI.getName(Read.RegisterID) << '\n');
5010b57cec5SDimitry Andric   }
5020b57cec5SDimitry Andric 
5030b57cec5SDimitry Andric   CurrentUse += NumImplicitUses;
5040b57cec5SDimitry Andric 
505fe6060f1SDimitry Andric   bool AssumeDefsOnly = MCDesc.variadicOpsAreDefs();
5060b57cec5SDimitry Andric   for (unsigned I = 0, OpIndex = MCDesc.getNumOperands();
5070b57cec5SDimitry Andric        I < NumVariadicOps && !AssumeDefsOnly; ++I, ++OpIndex) {
5080b57cec5SDimitry Andric     const MCOperand &Op = MCI.getOperand(OpIndex);
5090b57cec5SDimitry Andric     if (!Op.isReg())
5100b57cec5SDimitry Andric       continue;
5110b57cec5SDimitry Andric 
5120b57cec5SDimitry Andric     ReadDescriptor &Read = ID.Reads[CurrentUse];
5130b57cec5SDimitry Andric     Read.OpIndex = OpIndex;
5140b57cec5SDimitry Andric     Read.UseIndex = NumExplicitUses + NumImplicitUses + I;
5150b57cec5SDimitry Andric     Read.SchedClassID = SchedClassID;
5160b57cec5SDimitry Andric     ++CurrentUse;
5170b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\t\t[Use][V] OpIdx=" << Read.OpIndex
5180b57cec5SDimitry Andric                       << ", UseIndex=" << Read.UseIndex << '\n');
5190b57cec5SDimitry Andric   }
5200b57cec5SDimitry Andric 
5210b57cec5SDimitry Andric   ID.Reads.resize(CurrentUse);
5220b57cec5SDimitry Andric }
5230b57cec5SDimitry Andric 
5240b57cec5SDimitry Andric Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID,
5250b57cec5SDimitry Andric                                     const MCInst &MCI) const {
5260b57cec5SDimitry Andric   if (ID.NumMicroOps != 0)
5270b57cec5SDimitry Andric     return ErrorSuccess();
5280b57cec5SDimitry Andric 
5298bcb0991SDimitry Andric   bool UsesBuffers = ID.UsedBuffers;
5300b57cec5SDimitry Andric   bool UsesResources = !ID.Resources.empty();
5315ffd83dbSDimitry Andric   if (!UsesBuffers && !UsesResources)
5320b57cec5SDimitry Andric     return ErrorSuccess();
5330b57cec5SDimitry Andric 
5345ffd83dbSDimitry Andric   // FIXME: see PR44797. We should revisit these checks and possibly move them
5355ffd83dbSDimitry Andric   // in CodeGenSchedule.cpp.
5365ffd83dbSDimitry Andric   StringRef Message = "found an inconsistent instruction that decodes to zero "
5375ffd83dbSDimitry Andric                       "opcodes and that consumes scheduler resources.";
5385ffd83dbSDimitry Andric   return make_error<InstructionError<MCInst>>(std::string(Message), MCI);
5390b57cec5SDimitry Andric }
5400b57cec5SDimitry Andric 
5410b57cec5SDimitry Andric Expected<const InstrDesc &>
5420b57cec5SDimitry Andric InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
5430b57cec5SDimitry Andric   assert(STI.getSchedModel().hasInstrSchedModel() &&
5440b57cec5SDimitry Andric          "Itineraries are not yet supported!");
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric   // Obtain the instruction descriptor from the opcode.
5470b57cec5SDimitry Andric   unsigned short Opcode = MCI.getOpcode();
5480b57cec5SDimitry Andric   const MCInstrDesc &MCDesc = MCII.get(Opcode);
5490b57cec5SDimitry Andric   const MCSchedModel &SM = STI.getSchedModel();
5500b57cec5SDimitry Andric 
5510b57cec5SDimitry Andric   // Then obtain the scheduling class information from the instruction.
5520b57cec5SDimitry Andric   unsigned SchedClassID = MCDesc.getSchedClass();
5530b57cec5SDimitry Andric   bool IsVariant = SM.getSchedClassDesc(SchedClassID)->isVariant();
5540b57cec5SDimitry Andric 
5550b57cec5SDimitry Andric   // Try to solve variant scheduling classes.
5560b57cec5SDimitry Andric   if (IsVariant) {
5570b57cec5SDimitry Andric     unsigned CPUID = SM.getProcessorID();
5580b57cec5SDimitry Andric     while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
559e8d8bef9SDimitry Andric       SchedClassID =
560e8d8bef9SDimitry Andric           STI.resolveVariantSchedClass(SchedClassID, &MCI, &MCII, CPUID);
5610b57cec5SDimitry Andric 
5620b57cec5SDimitry Andric     if (!SchedClassID) {
5630b57cec5SDimitry Andric       return make_error<InstructionError<MCInst>>(
5640b57cec5SDimitry Andric           "unable to resolve scheduling class for write variant.", MCI);
5650b57cec5SDimitry Andric     }
5660b57cec5SDimitry Andric   }
5670b57cec5SDimitry Andric 
5680b57cec5SDimitry Andric   // Check if this instruction is supported. Otherwise, report an error.
5690b57cec5SDimitry Andric   const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
5700b57cec5SDimitry Andric   if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
5710b57cec5SDimitry Andric     return make_error<InstructionError<MCInst>>(
5720b57cec5SDimitry Andric         "found an unsupported instruction in the input assembly sequence.",
5730b57cec5SDimitry Andric         MCI);
5740b57cec5SDimitry Andric   }
5750b57cec5SDimitry Andric 
5760b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "\n\t\tOpcode Name= " << MCII.getName(Opcode) << '\n');
5770b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "\t\tSchedClassID=" << SchedClassID << '\n');
578*81ad6265SDimitry Andric   LLVM_DEBUG(dbgs() << "\t\tOpcode=" << Opcode << '\n');
5790b57cec5SDimitry Andric 
5800b57cec5SDimitry Andric   // Create a new empty descriptor.
5818bcb0991SDimitry Andric   std::unique_ptr<InstrDesc> ID = std::make_unique<InstrDesc>();
5820b57cec5SDimitry Andric   ID->NumMicroOps = SCDesc.NumMicroOps;
5830b57cec5SDimitry Andric   ID->SchedClassID = SchedClassID;
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric   if (MCDesc.isCall() && FirstCallInst) {
5860b57cec5SDimitry Andric     // We don't correctly model calls.
5870b57cec5SDimitry Andric     WithColor::warning() << "found a call in the input assembly sequence.\n";
5880b57cec5SDimitry Andric     WithColor::note() << "call instructions are not correctly modeled. "
5890b57cec5SDimitry Andric                       << "Assume a latency of 100cy.\n";
5900b57cec5SDimitry Andric     FirstCallInst = false;
5910b57cec5SDimitry Andric   }
5920b57cec5SDimitry Andric 
5930b57cec5SDimitry Andric   if (MCDesc.isReturn() && FirstReturnInst) {
5940b57cec5SDimitry Andric     WithColor::warning() << "found a return instruction in the input"
5950b57cec5SDimitry Andric                          << " assembly sequence.\n";
5960b57cec5SDimitry Andric     WithColor::note() << "program counter updates are ignored.\n";
5970b57cec5SDimitry Andric     FirstReturnInst = false;
5980b57cec5SDimitry Andric   }
5990b57cec5SDimitry Andric 
6000b57cec5SDimitry Andric   initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
6010b57cec5SDimitry Andric   computeMaxLatency(*ID, MCDesc, SCDesc, STI);
6020b57cec5SDimitry Andric 
6030b57cec5SDimitry Andric   if (Error Err = verifyOperands(MCDesc, MCI))
6040b57cec5SDimitry Andric     return std::move(Err);
6050b57cec5SDimitry Andric 
6060b57cec5SDimitry Andric   populateWrites(*ID, MCI, SchedClassID);
6070b57cec5SDimitry Andric   populateReads(*ID, MCI, SchedClassID);
6080b57cec5SDimitry Andric 
6090b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
6100b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
6110b57cec5SDimitry Andric 
6124824e7fdSDimitry Andric   // Validation check on the instruction descriptor.
6130b57cec5SDimitry Andric   if (Error Err = verifyInstrDesc(*ID, MCI))
6140b57cec5SDimitry Andric     return std::move(Err);
6150b57cec5SDimitry Andric 
6160b57cec5SDimitry Andric   // Now add the new descriptor.
6170b57cec5SDimitry Andric   bool IsVariadic = MCDesc.isVariadic();
618*81ad6265SDimitry Andric   if ((ID->IsRecyclable = !IsVariadic && !IsVariant)) {
6190b57cec5SDimitry Andric     Descriptors[MCI.getOpcode()] = std::move(ID);
6200b57cec5SDimitry Andric     return *Descriptors[MCI.getOpcode()];
6210b57cec5SDimitry Andric   }
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric   VariantDescriptors[&MCI] = std::move(ID);
6240b57cec5SDimitry Andric   return *VariantDescriptors[&MCI];
6250b57cec5SDimitry Andric }
6260b57cec5SDimitry Andric 
6270b57cec5SDimitry Andric Expected<const InstrDesc &>
6280b57cec5SDimitry Andric InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
6290b57cec5SDimitry Andric   if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
6300b57cec5SDimitry Andric     return *Descriptors[MCI.getOpcode()];
6310b57cec5SDimitry Andric 
6320b57cec5SDimitry Andric   if (VariantDescriptors.find(&MCI) != VariantDescriptors.end())
6330b57cec5SDimitry Andric     return *VariantDescriptors[&MCI];
6340b57cec5SDimitry Andric 
6350b57cec5SDimitry Andric   return createInstrDescImpl(MCI);
6360b57cec5SDimitry Andric }
6370b57cec5SDimitry Andric 
638*81ad6265SDimitry Andric STATISTIC(NumVariantInst, "Number of MCInsts that doesn't have static Desc");
639*81ad6265SDimitry Andric 
6400b57cec5SDimitry Andric Expected<std::unique_ptr<Instruction>>
6410b57cec5SDimitry Andric InstrBuilder::createInstruction(const MCInst &MCI) {
6420b57cec5SDimitry Andric   Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI);
6430b57cec5SDimitry Andric   if (!DescOrErr)
6440b57cec5SDimitry Andric     return DescOrErr.takeError();
6450b57cec5SDimitry Andric   const InstrDesc &D = *DescOrErr;
646*81ad6265SDimitry Andric   Instruction *NewIS = nullptr;
647*81ad6265SDimitry Andric   std::unique_ptr<Instruction> CreatedIS;
648*81ad6265SDimitry Andric   bool IsInstRecycled = false;
649*81ad6265SDimitry Andric 
650*81ad6265SDimitry Andric   if (!D.IsRecyclable)
651*81ad6265SDimitry Andric     ++NumVariantInst;
652*81ad6265SDimitry Andric 
653*81ad6265SDimitry Andric   if (D.IsRecyclable && InstRecycleCB) {
654*81ad6265SDimitry Andric     if (auto *I = InstRecycleCB(D)) {
655*81ad6265SDimitry Andric       NewIS = I;
656*81ad6265SDimitry Andric       NewIS->reset();
657*81ad6265SDimitry Andric       IsInstRecycled = true;
658*81ad6265SDimitry Andric     }
659*81ad6265SDimitry Andric   }
660*81ad6265SDimitry Andric   if (!IsInstRecycled) {
661*81ad6265SDimitry Andric     CreatedIS = std::make_unique<Instruction>(D, MCI.getOpcode());
662*81ad6265SDimitry Andric     NewIS = CreatedIS.get();
663*81ad6265SDimitry Andric   }
664*81ad6265SDimitry Andric 
665*81ad6265SDimitry Andric   const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
666*81ad6265SDimitry Andric   const MCSchedClassDesc &SCDesc =
667*81ad6265SDimitry Andric       *STI.getSchedModel().getSchedClassDesc(D.SchedClassID);
668*81ad6265SDimitry Andric 
669*81ad6265SDimitry Andric   NewIS->setMayLoad(MCDesc.mayLoad());
670*81ad6265SDimitry Andric   NewIS->setMayStore(MCDesc.mayStore());
671*81ad6265SDimitry Andric   NewIS->setHasSideEffects(MCDesc.hasUnmodeledSideEffects());
672*81ad6265SDimitry Andric   NewIS->setBeginGroup(SCDesc.BeginGroup);
673*81ad6265SDimitry Andric   NewIS->setEndGroup(SCDesc.EndGroup);
674*81ad6265SDimitry Andric   NewIS->setRetireOOO(SCDesc.RetireOOO);
6750b57cec5SDimitry Andric 
6760b57cec5SDimitry Andric   // Check if this is a dependency breaking instruction.
6770b57cec5SDimitry Andric   APInt Mask;
6780b57cec5SDimitry Andric 
6790b57cec5SDimitry Andric   bool IsZeroIdiom = false;
6800b57cec5SDimitry Andric   bool IsDepBreaking = false;
6810b57cec5SDimitry Andric   if (MCIA) {
6820b57cec5SDimitry Andric     unsigned ProcID = STI.getSchedModel().getProcessorID();
6830b57cec5SDimitry Andric     IsZeroIdiom = MCIA->isZeroIdiom(MCI, Mask, ProcID);
6840b57cec5SDimitry Andric     IsDepBreaking =
6850b57cec5SDimitry Andric         IsZeroIdiom || MCIA->isDependencyBreaking(MCI, Mask, ProcID);
6860b57cec5SDimitry Andric     if (MCIA->isOptimizableRegisterMove(MCI, ProcID))
6870b57cec5SDimitry Andric       NewIS->setOptimizableMove();
6880b57cec5SDimitry Andric   }
6890b57cec5SDimitry Andric 
6900b57cec5SDimitry Andric   // Initialize Reads first.
6918bcb0991SDimitry Andric   MCPhysReg RegID = 0;
692*81ad6265SDimitry Andric   size_t Idx = 0U;
6930b57cec5SDimitry Andric   for (const ReadDescriptor &RD : D.Reads) {
6940b57cec5SDimitry Andric     if (!RD.isImplicitRead()) {
6950b57cec5SDimitry Andric       // explicit read.
6960b57cec5SDimitry Andric       const MCOperand &Op = MCI.getOperand(RD.OpIndex);
6970b57cec5SDimitry Andric       // Skip non-register operands.
6980b57cec5SDimitry Andric       if (!Op.isReg())
6990b57cec5SDimitry Andric         continue;
7000b57cec5SDimitry Andric       RegID = Op.getReg();
7010b57cec5SDimitry Andric     } else {
7020b57cec5SDimitry Andric       // Implicit read.
7030b57cec5SDimitry Andric       RegID = RD.RegisterID;
7040b57cec5SDimitry Andric     }
7050b57cec5SDimitry Andric 
7060b57cec5SDimitry Andric     // Skip invalid register operands.
7070b57cec5SDimitry Andric     if (!RegID)
7080b57cec5SDimitry Andric       continue;
7090b57cec5SDimitry Andric 
7100b57cec5SDimitry Andric     // Okay, this is a register operand. Create a ReadState for it.
711*81ad6265SDimitry Andric     ReadState *RS = nullptr;
712*81ad6265SDimitry Andric     if (IsInstRecycled && Idx < NewIS->getUses().size()) {
713*81ad6265SDimitry Andric       NewIS->getUses()[Idx] = ReadState(RD, RegID);
714*81ad6265SDimitry Andric       RS = &NewIS->getUses()[Idx++];
715*81ad6265SDimitry Andric     } else {
7160b57cec5SDimitry Andric       NewIS->getUses().emplace_back(RD, RegID);
717*81ad6265SDimitry Andric       RS = &NewIS->getUses().back();
718*81ad6265SDimitry Andric       ++Idx;
719*81ad6265SDimitry Andric     }
7200b57cec5SDimitry Andric 
7210b57cec5SDimitry Andric     if (IsDepBreaking) {
7220b57cec5SDimitry Andric       // A mask of all zeroes means: explicit input operands are not
7230b57cec5SDimitry Andric       // independent.
724349cc55cSDimitry Andric       if (Mask.isZero()) {
7250b57cec5SDimitry Andric         if (!RD.isImplicitRead())
726*81ad6265SDimitry Andric           RS->setIndependentFromDef();
7270b57cec5SDimitry Andric       } else {
7280b57cec5SDimitry Andric         // Check if this register operand is independent according to `Mask`.
7290b57cec5SDimitry Andric         // Note that Mask may not have enough bits to describe all explicit and
7300b57cec5SDimitry Andric         // implicit input operands. If this register operand doesn't have a
7310b57cec5SDimitry Andric         // corresponding bit in Mask, then conservatively assume that it is
7320b57cec5SDimitry Andric         // dependent.
7330b57cec5SDimitry Andric         if (Mask.getBitWidth() > RD.UseIndex) {
7340b57cec5SDimitry Andric           // Okay. This map describe register use `RD.UseIndex`.
7350b57cec5SDimitry Andric           if (Mask[RD.UseIndex])
736*81ad6265SDimitry Andric             RS->setIndependentFromDef();
7370b57cec5SDimitry Andric         }
7380b57cec5SDimitry Andric       }
7390b57cec5SDimitry Andric     }
7400b57cec5SDimitry Andric   }
741*81ad6265SDimitry Andric   if (IsInstRecycled && Idx < NewIS->getUses().size())
742*81ad6265SDimitry Andric     NewIS->getUses().pop_back_n(NewIS->getUses().size() - Idx);
7430b57cec5SDimitry Andric 
7440b57cec5SDimitry Andric   // Early exit if there are no writes.
745*81ad6265SDimitry Andric   if (D.Writes.empty()) {
746*81ad6265SDimitry Andric     if (IsInstRecycled)
747*81ad6265SDimitry Andric       return llvm::make_error<RecycledInstErr>(NewIS);
748*81ad6265SDimitry Andric     else
749*81ad6265SDimitry Andric       return std::move(CreatedIS);
750*81ad6265SDimitry Andric   }
7510b57cec5SDimitry Andric 
7520b57cec5SDimitry Andric   // Track register writes that implicitly clear the upper portion of the
7530b57cec5SDimitry Andric   // underlying super-registers using an APInt.
7540b57cec5SDimitry Andric   APInt WriteMask(D.Writes.size(), 0);
7550b57cec5SDimitry Andric 
7560b57cec5SDimitry Andric   // Now query the MCInstrAnalysis object to obtain information about which
7570b57cec5SDimitry Andric   // register writes implicitly clear the upper portion of a super-register.
7580b57cec5SDimitry Andric   if (MCIA)
7590b57cec5SDimitry Andric     MCIA->clearsSuperRegisters(MRI, MCI, WriteMask);
7600b57cec5SDimitry Andric 
7610b57cec5SDimitry Andric   // Initialize writes.
7620b57cec5SDimitry Andric   unsigned WriteIndex = 0;
763*81ad6265SDimitry Andric   Idx = 0U;
7640b57cec5SDimitry Andric   for (const WriteDescriptor &WD : D.Writes) {
7658bcb0991SDimitry Andric     RegID = WD.isImplicitWrite() ? WD.RegisterID
7660b57cec5SDimitry Andric                                  : MCI.getOperand(WD.OpIndex).getReg();
7670b57cec5SDimitry Andric     // Check if this is a optional definition that references NoReg.
7680b57cec5SDimitry Andric     if (WD.IsOptionalDef && !RegID) {
7690b57cec5SDimitry Andric       ++WriteIndex;
7700b57cec5SDimitry Andric       continue;
7710b57cec5SDimitry Andric     }
7720b57cec5SDimitry Andric 
7730b57cec5SDimitry Andric     assert(RegID && "Expected a valid register ID!");
774*81ad6265SDimitry Andric     if (IsInstRecycled && Idx < NewIS->getDefs().size()) {
775*81ad6265SDimitry Andric       NewIS->getDefs()[Idx++] =
776*81ad6265SDimitry Andric           WriteState(WD, RegID,
777*81ad6265SDimitry Andric                      /* ClearsSuperRegs */ WriteMask[WriteIndex],
778*81ad6265SDimitry Andric                      /* WritesZero */ IsZeroIdiom);
779*81ad6265SDimitry Andric     } else {
7800b57cec5SDimitry Andric       NewIS->getDefs().emplace_back(WD, RegID,
7810b57cec5SDimitry Andric                                     /* ClearsSuperRegs */ WriteMask[WriteIndex],
7820b57cec5SDimitry Andric                                     /* WritesZero */ IsZeroIdiom);
783*81ad6265SDimitry Andric       ++Idx;
784*81ad6265SDimitry Andric     }
7850b57cec5SDimitry Andric     ++WriteIndex;
7860b57cec5SDimitry Andric   }
787*81ad6265SDimitry Andric   if (IsInstRecycled && Idx < NewIS->getDefs().size())
788*81ad6265SDimitry Andric     NewIS->getDefs().pop_back_n(NewIS->getDefs().size() - Idx);
7890b57cec5SDimitry Andric 
790*81ad6265SDimitry Andric   if (IsInstRecycled)
791*81ad6265SDimitry Andric     return llvm::make_error<RecycledInstErr>(NewIS);
792*81ad6265SDimitry Andric   else
793*81ad6265SDimitry Andric     return std::move(CreatedIS);
7940b57cec5SDimitry Andric }
7950b57cec5SDimitry Andric } // namespace mca
7960b57cec5SDimitry Andric } // namespace llvm
797