106f32e7eSjoerg //===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg /// \file
906f32e7eSjoerg ///
1006f32e7eSjoerg /// This file implements the InstrBuilder interface.
1106f32e7eSjoerg ///
1206f32e7eSjoerg //===----------------------------------------------------------------------===//
1306f32e7eSjoerg
1406f32e7eSjoerg #include "llvm/MCA/InstrBuilder.h"
1506f32e7eSjoerg #include "llvm/ADT/APInt.h"
1606f32e7eSjoerg #include "llvm/ADT/DenseMap.h"
1706f32e7eSjoerg #include "llvm/MC/MCInst.h"
1806f32e7eSjoerg #include "llvm/Support/Debug.h"
1906f32e7eSjoerg #include "llvm/Support/WithColor.h"
2006f32e7eSjoerg #include "llvm/Support/raw_ostream.h"
2106f32e7eSjoerg
2206f32e7eSjoerg #define DEBUG_TYPE "llvm-mca"
2306f32e7eSjoerg
2406f32e7eSjoerg namespace llvm {
2506f32e7eSjoerg namespace mca {
2606f32e7eSjoerg
InstrBuilder(const llvm::MCSubtargetInfo & sti,const llvm::MCInstrInfo & mcii,const llvm::MCRegisterInfo & mri,const llvm::MCInstrAnalysis * mcia)2706f32e7eSjoerg InstrBuilder::InstrBuilder(const llvm::MCSubtargetInfo &sti,
2806f32e7eSjoerg const llvm::MCInstrInfo &mcii,
2906f32e7eSjoerg const llvm::MCRegisterInfo &mri,
3006f32e7eSjoerg const llvm::MCInstrAnalysis *mcia)
3106f32e7eSjoerg : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia), FirstCallInst(true),
3206f32e7eSjoerg FirstReturnInst(true) {
3306f32e7eSjoerg const MCSchedModel &SM = STI.getSchedModel();
3406f32e7eSjoerg ProcResourceMasks.resize(SM.getNumProcResourceKinds());
3506f32e7eSjoerg computeProcResourceMasks(STI.getSchedModel(), ProcResourceMasks);
3606f32e7eSjoerg }
3706f32e7eSjoerg
initializeUsedResources(InstrDesc & ID,const MCSchedClassDesc & SCDesc,const MCSubtargetInfo & STI,ArrayRef<uint64_t> ProcResourceMasks)3806f32e7eSjoerg static void initializeUsedResources(InstrDesc &ID,
3906f32e7eSjoerg const MCSchedClassDesc &SCDesc,
4006f32e7eSjoerg const MCSubtargetInfo &STI,
4106f32e7eSjoerg ArrayRef<uint64_t> ProcResourceMasks) {
4206f32e7eSjoerg const MCSchedModel &SM = STI.getSchedModel();
4306f32e7eSjoerg
4406f32e7eSjoerg // Populate resources consumed.
4506f32e7eSjoerg using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
4606f32e7eSjoerg std::vector<ResourcePlusCycles> Worklist;
4706f32e7eSjoerg
4806f32e7eSjoerg // Track cycles contributed by resources that are in a "Super" relationship.
4906f32e7eSjoerg // This is required if we want to correctly match the behavior of method
5006f32e7eSjoerg // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
5106f32e7eSjoerg // of "consumed" processor resources and resource cycles, the logic in
5206f32e7eSjoerg // ExpandProcResource() doesn't update the number of resource cycles
5306f32e7eSjoerg // contributed by a "Super" resource to a group.
5406f32e7eSjoerg // We need to take this into account when we find that a processor resource is
5506f32e7eSjoerg // part of a group, and it is also used as the "Super" of other resources.
5606f32e7eSjoerg // This map stores the number of cycles contributed by sub-resources that are
5706f32e7eSjoerg // part of a "Super" resource. The key value is the "Super" resource mask ID.
5806f32e7eSjoerg DenseMap<uint64_t, unsigned> SuperResources;
5906f32e7eSjoerg
6006f32e7eSjoerg unsigned NumProcResources = SM.getNumProcResourceKinds();
6106f32e7eSjoerg APInt Buffers(NumProcResources, 0);
6206f32e7eSjoerg
6306f32e7eSjoerg bool AllInOrderResources = true;
6406f32e7eSjoerg bool AnyDispatchHazards = false;
6506f32e7eSjoerg for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
6606f32e7eSjoerg const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
6706f32e7eSjoerg const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
6806f32e7eSjoerg if (!PRE->Cycles) {
6906f32e7eSjoerg #ifndef NDEBUG
7006f32e7eSjoerg WithColor::warning()
7106f32e7eSjoerg << "Ignoring invalid write of zero cycles on processor resource "
7206f32e7eSjoerg << PR.Name << "\n";
7306f32e7eSjoerg WithColor::note() << "found in scheduling class " << SCDesc.Name
7406f32e7eSjoerg << " (write index #" << I << ")\n";
7506f32e7eSjoerg #endif
7606f32e7eSjoerg continue;
7706f32e7eSjoerg }
7806f32e7eSjoerg
7906f32e7eSjoerg uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
8006f32e7eSjoerg if (PR.BufferSize < 0) {
8106f32e7eSjoerg AllInOrderResources = false;
8206f32e7eSjoerg } else {
8306f32e7eSjoerg Buffers.setBit(getResourceStateIndex(Mask));
8406f32e7eSjoerg AnyDispatchHazards |= (PR.BufferSize == 0);
8506f32e7eSjoerg AllInOrderResources &= (PR.BufferSize <= 1);
8606f32e7eSjoerg }
8706f32e7eSjoerg
8806f32e7eSjoerg CycleSegment RCy(0, PRE->Cycles, false);
8906f32e7eSjoerg Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
9006f32e7eSjoerg if (PR.SuperIdx) {
9106f32e7eSjoerg uint64_t Super = ProcResourceMasks[PR.SuperIdx];
9206f32e7eSjoerg SuperResources[Super] += PRE->Cycles;
9306f32e7eSjoerg }
9406f32e7eSjoerg }
9506f32e7eSjoerg
9606f32e7eSjoerg ID.MustIssueImmediately = AllInOrderResources && AnyDispatchHazards;
9706f32e7eSjoerg
9806f32e7eSjoerg // Sort elements by mask popcount, so that we prioritize resource units over
9906f32e7eSjoerg // resource groups, and smaller groups over larger groups.
10006f32e7eSjoerg sort(Worklist, [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
10106f32e7eSjoerg unsigned popcntA = countPopulation(A.first);
10206f32e7eSjoerg unsigned popcntB = countPopulation(B.first);
10306f32e7eSjoerg if (popcntA < popcntB)
10406f32e7eSjoerg return true;
10506f32e7eSjoerg if (popcntA > popcntB)
10606f32e7eSjoerg return false;
10706f32e7eSjoerg return A.first < B.first;
10806f32e7eSjoerg });
10906f32e7eSjoerg
11006f32e7eSjoerg uint64_t UsedResourceUnits = 0;
11106f32e7eSjoerg uint64_t UsedResourceGroups = 0;
11206f32e7eSjoerg
11306f32e7eSjoerg // Remove cycles contributed by smaller resources.
11406f32e7eSjoerg for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
11506f32e7eSjoerg ResourcePlusCycles &A = Worklist[I];
11606f32e7eSjoerg if (!A.second.size()) {
11706f32e7eSjoerg assert(countPopulation(A.first) > 1 && "Expected a group!");
11806f32e7eSjoerg UsedResourceGroups |= PowerOf2Floor(A.first);
11906f32e7eSjoerg continue;
12006f32e7eSjoerg }
12106f32e7eSjoerg
12206f32e7eSjoerg ID.Resources.emplace_back(A);
12306f32e7eSjoerg uint64_t NormalizedMask = A.first;
12406f32e7eSjoerg if (countPopulation(A.first) == 1) {
12506f32e7eSjoerg UsedResourceUnits |= A.first;
12606f32e7eSjoerg } else {
12706f32e7eSjoerg // Remove the leading 1 from the resource group mask.
12806f32e7eSjoerg NormalizedMask ^= PowerOf2Floor(NormalizedMask);
12906f32e7eSjoerg UsedResourceGroups |= (A.first ^ NormalizedMask);
13006f32e7eSjoerg }
13106f32e7eSjoerg
13206f32e7eSjoerg for (unsigned J = I + 1; J < E; ++J) {
13306f32e7eSjoerg ResourcePlusCycles &B = Worklist[J];
13406f32e7eSjoerg if ((NormalizedMask & B.first) == NormalizedMask) {
13506f32e7eSjoerg B.second.CS.subtract(A.second.size() - SuperResources[A.first]);
13606f32e7eSjoerg if (countPopulation(B.first) > 1)
13706f32e7eSjoerg B.second.NumUnits++;
13806f32e7eSjoerg }
13906f32e7eSjoerg }
14006f32e7eSjoerg }
14106f32e7eSjoerg
14206f32e7eSjoerg // A SchedWrite may specify a number of cycles in which a resource group
14306f32e7eSjoerg // is reserved. For example (on target x86; cpu Haswell):
14406f32e7eSjoerg //
14506f32e7eSjoerg // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
14606f32e7eSjoerg // let ResourceCycles = [2, 2, 3];
14706f32e7eSjoerg // }
14806f32e7eSjoerg //
14906f32e7eSjoerg // This means:
15006f32e7eSjoerg // Resource units HWPort0 and HWPort1 are both used for 2cy.
15106f32e7eSjoerg // Resource group HWPort01 is the union of HWPort0 and HWPort1.
15206f32e7eSjoerg // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
15306f32e7eSjoerg // will not be usable for 2 entire cycles from instruction issue.
15406f32e7eSjoerg //
15506f32e7eSjoerg // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
15606f32e7eSjoerg // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
15706f32e7eSjoerg // extra delay on top of the 2 cycles latency.
15806f32e7eSjoerg // During those extra cycles, HWPort01 is not usable by other instructions.
15906f32e7eSjoerg for (ResourcePlusCycles &RPC : ID.Resources) {
16006f32e7eSjoerg if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
16106f32e7eSjoerg // Remove the leading 1 from the resource group mask.
16206f32e7eSjoerg uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
163*da58b97aSjoerg uint64_t MaxResourceUnits = countPopulation(Mask);
164*da58b97aSjoerg if (RPC.second.NumUnits > countPopulation(Mask)) {
16506f32e7eSjoerg RPC.second.setReserved();
166*da58b97aSjoerg RPC.second.NumUnits = MaxResourceUnits;
167*da58b97aSjoerg }
16806f32e7eSjoerg }
16906f32e7eSjoerg }
17006f32e7eSjoerg
17106f32e7eSjoerg // Identify extra buffers that are consumed through super resources.
17206f32e7eSjoerg for (const std::pair<uint64_t, unsigned> &SR : SuperResources) {
17306f32e7eSjoerg for (unsigned I = 1, E = NumProcResources; I < E; ++I) {
17406f32e7eSjoerg const MCProcResourceDesc &PR = *SM.getProcResource(I);
17506f32e7eSjoerg if (PR.BufferSize == -1)
17606f32e7eSjoerg continue;
17706f32e7eSjoerg
17806f32e7eSjoerg uint64_t Mask = ProcResourceMasks[I];
17906f32e7eSjoerg if (Mask != SR.first && ((Mask & SR.first) == SR.first))
18006f32e7eSjoerg Buffers.setBit(getResourceStateIndex(Mask));
18106f32e7eSjoerg }
18206f32e7eSjoerg }
18306f32e7eSjoerg
18406f32e7eSjoerg ID.UsedBuffers = Buffers.getZExtValue();
18506f32e7eSjoerg ID.UsedProcResUnits = UsedResourceUnits;
18606f32e7eSjoerg ID.UsedProcResGroups = UsedResourceGroups;
18706f32e7eSjoerg
18806f32e7eSjoerg LLVM_DEBUG({
18906f32e7eSjoerg for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
19006f32e7eSjoerg dbgs() << "\t\tResource Mask=" << format_hex(R.first, 16) << ", "
19106f32e7eSjoerg << "Reserved=" << R.second.isReserved() << ", "
19206f32e7eSjoerg << "#Units=" << R.second.NumUnits << ", "
19306f32e7eSjoerg << "cy=" << R.second.size() << '\n';
19406f32e7eSjoerg uint64_t BufferIDs = ID.UsedBuffers;
19506f32e7eSjoerg while (BufferIDs) {
19606f32e7eSjoerg uint64_t Current = BufferIDs & (-BufferIDs);
19706f32e7eSjoerg dbgs() << "\t\tBuffer Mask=" << format_hex(Current, 16) << '\n';
19806f32e7eSjoerg BufferIDs ^= Current;
19906f32e7eSjoerg }
20006f32e7eSjoerg dbgs() << "\t\t Used Units=" << format_hex(ID.UsedProcResUnits, 16) << '\n';
20106f32e7eSjoerg dbgs() << "\t\tUsed Groups=" << format_hex(ID.UsedProcResGroups, 16)
20206f32e7eSjoerg << '\n';
20306f32e7eSjoerg });
20406f32e7eSjoerg }
20506f32e7eSjoerg
computeMaxLatency(InstrDesc & ID,const MCInstrDesc & MCDesc,const MCSchedClassDesc & SCDesc,const MCSubtargetInfo & STI)20606f32e7eSjoerg static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
20706f32e7eSjoerg const MCSchedClassDesc &SCDesc,
20806f32e7eSjoerg const MCSubtargetInfo &STI) {
20906f32e7eSjoerg if (MCDesc.isCall()) {
21006f32e7eSjoerg // We cannot estimate how long this call will take.
21106f32e7eSjoerg // Artificially set an arbitrarily high latency (100cy).
21206f32e7eSjoerg ID.MaxLatency = 100U;
21306f32e7eSjoerg return;
21406f32e7eSjoerg }
21506f32e7eSjoerg
21606f32e7eSjoerg int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
21706f32e7eSjoerg // If latency is unknown, then conservatively assume a MaxLatency of 100cy.
21806f32e7eSjoerg ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
21906f32e7eSjoerg }
22006f32e7eSjoerg
verifyOperands(const MCInstrDesc & MCDesc,const MCInst & MCI)22106f32e7eSjoerg static Error verifyOperands(const MCInstrDesc &MCDesc, const MCInst &MCI) {
22206f32e7eSjoerg // Count register definitions, and skip non register operands in the process.
22306f32e7eSjoerg unsigned I, E;
22406f32e7eSjoerg unsigned NumExplicitDefs = MCDesc.getNumDefs();
22506f32e7eSjoerg for (I = 0, E = MCI.getNumOperands(); NumExplicitDefs && I < E; ++I) {
22606f32e7eSjoerg const MCOperand &Op = MCI.getOperand(I);
22706f32e7eSjoerg if (Op.isReg())
22806f32e7eSjoerg --NumExplicitDefs;
22906f32e7eSjoerg }
23006f32e7eSjoerg
23106f32e7eSjoerg if (NumExplicitDefs) {
23206f32e7eSjoerg return make_error<InstructionError<MCInst>>(
23306f32e7eSjoerg "Expected more register operand definitions.", MCI);
23406f32e7eSjoerg }
23506f32e7eSjoerg
23606f32e7eSjoerg if (MCDesc.hasOptionalDef()) {
23706f32e7eSjoerg // Always assume that the optional definition is the last operand.
23806f32e7eSjoerg const MCOperand &Op = MCI.getOperand(MCDesc.getNumOperands() - 1);
23906f32e7eSjoerg if (I == MCI.getNumOperands() || !Op.isReg()) {
24006f32e7eSjoerg std::string Message =
24106f32e7eSjoerg "expected a register operand for an optional definition. Instruction "
24206f32e7eSjoerg "has not been correctly analyzed.";
24306f32e7eSjoerg return make_error<InstructionError<MCInst>>(Message, MCI);
24406f32e7eSjoerg }
24506f32e7eSjoerg }
24606f32e7eSjoerg
24706f32e7eSjoerg return ErrorSuccess();
24806f32e7eSjoerg }
24906f32e7eSjoerg
populateWrites(InstrDesc & ID,const MCInst & MCI,unsigned SchedClassID)25006f32e7eSjoerg void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
25106f32e7eSjoerg unsigned SchedClassID) {
25206f32e7eSjoerg const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
25306f32e7eSjoerg const MCSchedModel &SM = STI.getSchedModel();
25406f32e7eSjoerg const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
25506f32e7eSjoerg
25606f32e7eSjoerg // Assumptions made by this algorithm:
25706f32e7eSjoerg // 1. The number of explicit and implicit register definitions in a MCInst
25806f32e7eSjoerg // matches the number of explicit and implicit definitions according to
25906f32e7eSjoerg // the opcode descriptor (MCInstrDesc).
26006f32e7eSjoerg // 2. Uses start at index #(MCDesc.getNumDefs()).
26106f32e7eSjoerg // 3. There can only be a single optional register definition, an it is
262*da58b97aSjoerg // either the last operand of the sequence (excluding extra operands
263*da58b97aSjoerg // contributed by variadic opcodes) or one of the explicit register
264*da58b97aSjoerg // definitions. The latter occurs for some Thumb1 instructions.
26506f32e7eSjoerg //
26606f32e7eSjoerg // These assumptions work quite well for most out-of-order in-tree targets
26706f32e7eSjoerg // like x86. This is mainly because the vast majority of instructions is
26806f32e7eSjoerg // expanded to MCInst using a straightforward lowering logic that preserves
26906f32e7eSjoerg // the ordering of the operands.
27006f32e7eSjoerg //
27106f32e7eSjoerg // About assumption 1.
27206f32e7eSjoerg // The algorithm allows non-register operands between register operand
27306f32e7eSjoerg // definitions. This helps to handle some special ARM instructions with
27406f32e7eSjoerg // implicit operand increment (-mtriple=armv7):
27506f32e7eSjoerg //
27606f32e7eSjoerg // vld1.32 {d18, d19}, [r1]! @ <MCInst #1463 VLD1q32wb_fixed
27706f32e7eSjoerg // @ <MCOperand Reg:59>
27806f32e7eSjoerg // @ <MCOperand Imm:0> (!!)
27906f32e7eSjoerg // @ <MCOperand Reg:67>
28006f32e7eSjoerg // @ <MCOperand Imm:0>
28106f32e7eSjoerg // @ <MCOperand Imm:14>
28206f32e7eSjoerg // @ <MCOperand Reg:0>>
28306f32e7eSjoerg //
28406f32e7eSjoerg // MCDesc reports:
28506f32e7eSjoerg // 6 explicit operands.
28606f32e7eSjoerg // 1 optional definition
28706f32e7eSjoerg // 2 explicit definitions (!!)
28806f32e7eSjoerg //
28906f32e7eSjoerg // The presence of an 'Imm' operand between the two register definitions
29006f32e7eSjoerg // breaks the assumption that "register definitions are always at the
29106f32e7eSjoerg // beginning of the operand sequence".
29206f32e7eSjoerg //
29306f32e7eSjoerg // To workaround this issue, this algorithm ignores (i.e. skips) any
29406f32e7eSjoerg // non-register operands between register definitions. The optional
29506f32e7eSjoerg // definition is still at index #(NumOperands-1).
29606f32e7eSjoerg //
29706f32e7eSjoerg // According to assumption 2. register reads start at #(NumExplicitDefs-1).
29806f32e7eSjoerg // That means, register R1 from the example is both read and written.
29906f32e7eSjoerg unsigned NumExplicitDefs = MCDesc.getNumDefs();
30006f32e7eSjoerg unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
30106f32e7eSjoerg unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
30206f32e7eSjoerg unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
30306f32e7eSjoerg if (MCDesc.hasOptionalDef())
30406f32e7eSjoerg TotalDefs++;
30506f32e7eSjoerg
30606f32e7eSjoerg unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands();
30706f32e7eSjoerg ID.Writes.resize(TotalDefs + NumVariadicOps);
30806f32e7eSjoerg // Iterate over the operands list, and skip non-register operands.
30906f32e7eSjoerg // The first NumExplicitDefs register operands are expected to be register
31006f32e7eSjoerg // definitions.
31106f32e7eSjoerg unsigned CurrentDef = 0;
312*da58b97aSjoerg unsigned OptionalDefIdx = MCDesc.getNumOperands() - 1;
31306f32e7eSjoerg unsigned i = 0;
31406f32e7eSjoerg for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
31506f32e7eSjoerg const MCOperand &Op = MCI.getOperand(i);
31606f32e7eSjoerg if (!Op.isReg())
31706f32e7eSjoerg continue;
31806f32e7eSjoerg
319*da58b97aSjoerg if (MCDesc.OpInfo[CurrentDef].isOptionalDef()) {
320*da58b97aSjoerg OptionalDefIdx = CurrentDef++;
321*da58b97aSjoerg continue;
322*da58b97aSjoerg }
323*da58b97aSjoerg
32406f32e7eSjoerg WriteDescriptor &Write = ID.Writes[CurrentDef];
32506f32e7eSjoerg Write.OpIndex = i;
32606f32e7eSjoerg if (CurrentDef < NumWriteLatencyEntries) {
32706f32e7eSjoerg const MCWriteLatencyEntry &WLE =
32806f32e7eSjoerg *STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
32906f32e7eSjoerg // Conservatively default to MaxLatency.
33006f32e7eSjoerg Write.Latency =
33106f32e7eSjoerg WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
33206f32e7eSjoerg Write.SClassOrWriteResourceID = WLE.WriteResourceID;
33306f32e7eSjoerg } else {
33406f32e7eSjoerg // Assign a default latency for this write.
33506f32e7eSjoerg Write.Latency = ID.MaxLatency;
33606f32e7eSjoerg Write.SClassOrWriteResourceID = 0;
33706f32e7eSjoerg }
33806f32e7eSjoerg Write.IsOptionalDef = false;
33906f32e7eSjoerg LLVM_DEBUG({
34006f32e7eSjoerg dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
34106f32e7eSjoerg << ", Latency=" << Write.Latency
34206f32e7eSjoerg << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
34306f32e7eSjoerg });
34406f32e7eSjoerg CurrentDef++;
34506f32e7eSjoerg }
34606f32e7eSjoerg
34706f32e7eSjoerg assert(CurrentDef == NumExplicitDefs &&
34806f32e7eSjoerg "Expected more register operand definitions.");
34906f32e7eSjoerg for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
35006f32e7eSjoerg unsigned Index = NumExplicitDefs + CurrentDef;
35106f32e7eSjoerg WriteDescriptor &Write = ID.Writes[Index];
35206f32e7eSjoerg Write.OpIndex = ~CurrentDef;
35306f32e7eSjoerg Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
35406f32e7eSjoerg if (Index < NumWriteLatencyEntries) {
35506f32e7eSjoerg const MCWriteLatencyEntry &WLE =
35606f32e7eSjoerg *STI.getWriteLatencyEntry(&SCDesc, Index);
35706f32e7eSjoerg // Conservatively default to MaxLatency.
35806f32e7eSjoerg Write.Latency =
35906f32e7eSjoerg WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
36006f32e7eSjoerg Write.SClassOrWriteResourceID = WLE.WriteResourceID;
36106f32e7eSjoerg } else {
36206f32e7eSjoerg // Assign a default latency for this write.
36306f32e7eSjoerg Write.Latency = ID.MaxLatency;
36406f32e7eSjoerg Write.SClassOrWriteResourceID = 0;
36506f32e7eSjoerg }
36606f32e7eSjoerg
36706f32e7eSjoerg Write.IsOptionalDef = false;
36806f32e7eSjoerg assert(Write.RegisterID != 0 && "Expected a valid phys register!");
36906f32e7eSjoerg LLVM_DEBUG({
37006f32e7eSjoerg dbgs() << "\t\t[Def][I] OpIdx=" << ~Write.OpIndex
37106f32e7eSjoerg << ", PhysReg=" << MRI.getName(Write.RegisterID)
37206f32e7eSjoerg << ", Latency=" << Write.Latency
37306f32e7eSjoerg << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
37406f32e7eSjoerg });
37506f32e7eSjoerg }
37606f32e7eSjoerg
37706f32e7eSjoerg if (MCDesc.hasOptionalDef()) {
37806f32e7eSjoerg WriteDescriptor &Write = ID.Writes[NumExplicitDefs + NumImplicitDefs];
379*da58b97aSjoerg Write.OpIndex = OptionalDefIdx;
38006f32e7eSjoerg // Assign a default latency for this write.
38106f32e7eSjoerg Write.Latency = ID.MaxLatency;
38206f32e7eSjoerg Write.SClassOrWriteResourceID = 0;
38306f32e7eSjoerg Write.IsOptionalDef = true;
38406f32e7eSjoerg LLVM_DEBUG({
38506f32e7eSjoerg dbgs() << "\t\t[Def][O] OpIdx=" << Write.OpIndex
38606f32e7eSjoerg << ", Latency=" << Write.Latency
38706f32e7eSjoerg << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
38806f32e7eSjoerg });
38906f32e7eSjoerg }
39006f32e7eSjoerg
39106f32e7eSjoerg if (!NumVariadicOps)
39206f32e7eSjoerg return;
39306f32e7eSjoerg
39406f32e7eSjoerg // FIXME: if an instruction opcode is flagged 'mayStore', and it has no
39506f32e7eSjoerg // "unmodeledSideEffects', then this logic optimistically assumes that any
39606f32e7eSjoerg // extra register operands in the variadic sequence is not a register
39706f32e7eSjoerg // definition.
39806f32e7eSjoerg //
39906f32e7eSjoerg // Otherwise, we conservatively assume that any register operand from the
40006f32e7eSjoerg // variadic sequence is both a register read and a register write.
40106f32e7eSjoerg bool AssumeUsesOnly = MCDesc.mayStore() && !MCDesc.mayLoad() &&
40206f32e7eSjoerg !MCDesc.hasUnmodeledSideEffects();
40306f32e7eSjoerg CurrentDef = NumExplicitDefs + NumImplicitDefs + MCDesc.hasOptionalDef();
40406f32e7eSjoerg for (unsigned I = 0, OpIndex = MCDesc.getNumOperands();
40506f32e7eSjoerg I < NumVariadicOps && !AssumeUsesOnly; ++I, ++OpIndex) {
40606f32e7eSjoerg const MCOperand &Op = MCI.getOperand(OpIndex);
40706f32e7eSjoerg if (!Op.isReg())
40806f32e7eSjoerg continue;
40906f32e7eSjoerg
41006f32e7eSjoerg WriteDescriptor &Write = ID.Writes[CurrentDef];
41106f32e7eSjoerg Write.OpIndex = OpIndex;
41206f32e7eSjoerg // Assign a default latency for this write.
41306f32e7eSjoerg Write.Latency = ID.MaxLatency;
41406f32e7eSjoerg Write.SClassOrWriteResourceID = 0;
41506f32e7eSjoerg Write.IsOptionalDef = false;
41606f32e7eSjoerg ++CurrentDef;
41706f32e7eSjoerg LLVM_DEBUG({
41806f32e7eSjoerg dbgs() << "\t\t[Def][V] OpIdx=" << Write.OpIndex
41906f32e7eSjoerg << ", Latency=" << Write.Latency
42006f32e7eSjoerg << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
42106f32e7eSjoerg });
42206f32e7eSjoerg }
42306f32e7eSjoerg
42406f32e7eSjoerg ID.Writes.resize(CurrentDef);
42506f32e7eSjoerg }
42606f32e7eSjoerg
populateReads(InstrDesc & ID,const MCInst & MCI,unsigned SchedClassID)42706f32e7eSjoerg void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
42806f32e7eSjoerg unsigned SchedClassID) {
42906f32e7eSjoerg const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
43006f32e7eSjoerg unsigned NumExplicitUses = MCDesc.getNumOperands() - MCDesc.getNumDefs();
43106f32e7eSjoerg unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
43206f32e7eSjoerg // Remove the optional definition.
43306f32e7eSjoerg if (MCDesc.hasOptionalDef())
43406f32e7eSjoerg --NumExplicitUses;
43506f32e7eSjoerg unsigned NumVariadicOps = MCI.getNumOperands() - MCDesc.getNumOperands();
43606f32e7eSjoerg unsigned TotalUses = NumExplicitUses + NumImplicitUses + NumVariadicOps;
43706f32e7eSjoerg ID.Reads.resize(TotalUses);
43806f32e7eSjoerg unsigned CurrentUse = 0;
43906f32e7eSjoerg for (unsigned I = 0, OpIndex = MCDesc.getNumDefs(); I < NumExplicitUses;
44006f32e7eSjoerg ++I, ++OpIndex) {
44106f32e7eSjoerg const MCOperand &Op = MCI.getOperand(OpIndex);
44206f32e7eSjoerg if (!Op.isReg())
44306f32e7eSjoerg continue;
44406f32e7eSjoerg
44506f32e7eSjoerg ReadDescriptor &Read = ID.Reads[CurrentUse];
44606f32e7eSjoerg Read.OpIndex = OpIndex;
44706f32e7eSjoerg Read.UseIndex = I;
44806f32e7eSjoerg Read.SchedClassID = SchedClassID;
44906f32e7eSjoerg ++CurrentUse;
45006f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex
45106f32e7eSjoerg << ", UseIndex=" << Read.UseIndex << '\n');
45206f32e7eSjoerg }
45306f32e7eSjoerg
45406f32e7eSjoerg // For the purpose of ReadAdvance, implicit uses come directly after explicit
45506f32e7eSjoerg // uses. The "UseIndex" must be updated according to that implicit layout.
45606f32e7eSjoerg for (unsigned I = 0; I < NumImplicitUses; ++I) {
45706f32e7eSjoerg ReadDescriptor &Read = ID.Reads[CurrentUse + I];
45806f32e7eSjoerg Read.OpIndex = ~I;
45906f32e7eSjoerg Read.UseIndex = NumExplicitUses + I;
46006f32e7eSjoerg Read.RegisterID = MCDesc.getImplicitUses()[I];
46106f32e7eSjoerg Read.SchedClassID = SchedClassID;
46206f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\t[Use][I] OpIdx=" << ~Read.OpIndex
46306f32e7eSjoerg << ", UseIndex=" << Read.UseIndex << ", RegisterID="
46406f32e7eSjoerg << MRI.getName(Read.RegisterID) << '\n');
46506f32e7eSjoerg }
46606f32e7eSjoerg
46706f32e7eSjoerg CurrentUse += NumImplicitUses;
46806f32e7eSjoerg
46906f32e7eSjoerg // FIXME: If an instruction opcode is marked as 'mayLoad', and it has no
47006f32e7eSjoerg // "unmodeledSideEffects", then this logic optimistically assumes that any
47106f32e7eSjoerg // extra register operand in the variadic sequence is not a register
47206f32e7eSjoerg // definition.
47306f32e7eSjoerg bool AssumeDefsOnly = !MCDesc.mayStore() && MCDesc.mayLoad() &&
47406f32e7eSjoerg !MCDesc.hasUnmodeledSideEffects();
47506f32e7eSjoerg for (unsigned I = 0, OpIndex = MCDesc.getNumOperands();
47606f32e7eSjoerg I < NumVariadicOps && !AssumeDefsOnly; ++I, ++OpIndex) {
47706f32e7eSjoerg const MCOperand &Op = MCI.getOperand(OpIndex);
47806f32e7eSjoerg if (!Op.isReg())
47906f32e7eSjoerg continue;
48006f32e7eSjoerg
48106f32e7eSjoerg ReadDescriptor &Read = ID.Reads[CurrentUse];
48206f32e7eSjoerg Read.OpIndex = OpIndex;
48306f32e7eSjoerg Read.UseIndex = NumExplicitUses + NumImplicitUses + I;
48406f32e7eSjoerg Read.SchedClassID = SchedClassID;
48506f32e7eSjoerg ++CurrentUse;
48606f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\t[Use][V] OpIdx=" << Read.OpIndex
48706f32e7eSjoerg << ", UseIndex=" << Read.UseIndex << '\n');
48806f32e7eSjoerg }
48906f32e7eSjoerg
49006f32e7eSjoerg ID.Reads.resize(CurrentUse);
49106f32e7eSjoerg }
49206f32e7eSjoerg
verifyInstrDesc(const InstrDesc & ID,const MCInst & MCI) const49306f32e7eSjoerg Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID,
49406f32e7eSjoerg const MCInst &MCI) const {
49506f32e7eSjoerg if (ID.NumMicroOps != 0)
49606f32e7eSjoerg return ErrorSuccess();
49706f32e7eSjoerg
49806f32e7eSjoerg bool UsesBuffers = ID.UsedBuffers;
49906f32e7eSjoerg bool UsesResources = !ID.Resources.empty();
500*da58b97aSjoerg if (!UsesBuffers && !UsesResources)
50106f32e7eSjoerg return ErrorSuccess();
50206f32e7eSjoerg
503*da58b97aSjoerg // FIXME: see PR44797. We should revisit these checks and possibly move them
504*da58b97aSjoerg // in CodeGenSchedule.cpp.
505*da58b97aSjoerg StringRef Message = "found an inconsistent instruction that decodes to zero "
506*da58b97aSjoerg "opcodes and that consumes scheduler resources.";
507*da58b97aSjoerg return make_error<InstructionError<MCInst>>(std::string(Message), MCI);
50806f32e7eSjoerg }
50906f32e7eSjoerg
51006f32e7eSjoerg Expected<const InstrDesc &>
createInstrDescImpl(const MCInst & MCI)51106f32e7eSjoerg InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
51206f32e7eSjoerg assert(STI.getSchedModel().hasInstrSchedModel() &&
51306f32e7eSjoerg "Itineraries are not yet supported!");
51406f32e7eSjoerg
51506f32e7eSjoerg // Obtain the instruction descriptor from the opcode.
51606f32e7eSjoerg unsigned short Opcode = MCI.getOpcode();
51706f32e7eSjoerg const MCInstrDesc &MCDesc = MCII.get(Opcode);
51806f32e7eSjoerg const MCSchedModel &SM = STI.getSchedModel();
51906f32e7eSjoerg
52006f32e7eSjoerg // Then obtain the scheduling class information from the instruction.
52106f32e7eSjoerg unsigned SchedClassID = MCDesc.getSchedClass();
52206f32e7eSjoerg bool IsVariant = SM.getSchedClassDesc(SchedClassID)->isVariant();
52306f32e7eSjoerg
52406f32e7eSjoerg // Try to solve variant scheduling classes.
52506f32e7eSjoerg if (IsVariant) {
52606f32e7eSjoerg unsigned CPUID = SM.getProcessorID();
52706f32e7eSjoerg while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
528*da58b97aSjoerg SchedClassID =
529*da58b97aSjoerg STI.resolveVariantSchedClass(SchedClassID, &MCI, &MCII, CPUID);
53006f32e7eSjoerg
53106f32e7eSjoerg if (!SchedClassID) {
53206f32e7eSjoerg return make_error<InstructionError<MCInst>>(
53306f32e7eSjoerg "unable to resolve scheduling class for write variant.", MCI);
53406f32e7eSjoerg }
53506f32e7eSjoerg }
53606f32e7eSjoerg
53706f32e7eSjoerg // Check if this instruction is supported. Otherwise, report an error.
53806f32e7eSjoerg const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
53906f32e7eSjoerg if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
54006f32e7eSjoerg return make_error<InstructionError<MCInst>>(
54106f32e7eSjoerg "found an unsupported instruction in the input assembly sequence.",
54206f32e7eSjoerg MCI);
54306f32e7eSjoerg }
54406f32e7eSjoerg
54506f32e7eSjoerg LLVM_DEBUG(dbgs() << "\n\t\tOpcode Name= " << MCII.getName(Opcode) << '\n');
54606f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\tSchedClassID=" << SchedClassID << '\n');
54706f32e7eSjoerg
54806f32e7eSjoerg // Create a new empty descriptor.
54906f32e7eSjoerg std::unique_ptr<InstrDesc> ID = std::make_unique<InstrDesc>();
55006f32e7eSjoerg ID->NumMicroOps = SCDesc.NumMicroOps;
55106f32e7eSjoerg ID->SchedClassID = SchedClassID;
55206f32e7eSjoerg
55306f32e7eSjoerg if (MCDesc.isCall() && FirstCallInst) {
55406f32e7eSjoerg // We don't correctly model calls.
55506f32e7eSjoerg WithColor::warning() << "found a call in the input assembly sequence.\n";
55606f32e7eSjoerg WithColor::note() << "call instructions are not correctly modeled. "
55706f32e7eSjoerg << "Assume a latency of 100cy.\n";
55806f32e7eSjoerg FirstCallInst = false;
55906f32e7eSjoerg }
56006f32e7eSjoerg
56106f32e7eSjoerg if (MCDesc.isReturn() && FirstReturnInst) {
56206f32e7eSjoerg WithColor::warning() << "found a return instruction in the input"
56306f32e7eSjoerg << " assembly sequence.\n";
56406f32e7eSjoerg WithColor::note() << "program counter updates are ignored.\n";
56506f32e7eSjoerg FirstReturnInst = false;
56606f32e7eSjoerg }
56706f32e7eSjoerg
56806f32e7eSjoerg ID->MayLoad = MCDesc.mayLoad();
56906f32e7eSjoerg ID->MayStore = MCDesc.mayStore();
57006f32e7eSjoerg ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects();
57106f32e7eSjoerg ID->BeginGroup = SCDesc.BeginGroup;
57206f32e7eSjoerg ID->EndGroup = SCDesc.EndGroup;
573*da58b97aSjoerg ID->RetireOOO = SCDesc.RetireOOO;
57406f32e7eSjoerg
57506f32e7eSjoerg initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
57606f32e7eSjoerg computeMaxLatency(*ID, MCDesc, SCDesc, STI);
57706f32e7eSjoerg
57806f32e7eSjoerg if (Error Err = verifyOperands(MCDesc, MCI))
57906f32e7eSjoerg return std::move(Err);
58006f32e7eSjoerg
58106f32e7eSjoerg populateWrites(*ID, MCI, SchedClassID);
58206f32e7eSjoerg populateReads(*ID, MCI, SchedClassID);
58306f32e7eSjoerg
58406f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
58506f32e7eSjoerg LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
58606f32e7eSjoerg
58706f32e7eSjoerg // Sanity check on the instruction descriptor.
58806f32e7eSjoerg if (Error Err = verifyInstrDesc(*ID, MCI))
58906f32e7eSjoerg return std::move(Err);
59006f32e7eSjoerg
59106f32e7eSjoerg // Now add the new descriptor.
59206f32e7eSjoerg bool IsVariadic = MCDesc.isVariadic();
59306f32e7eSjoerg if (!IsVariadic && !IsVariant) {
59406f32e7eSjoerg Descriptors[MCI.getOpcode()] = std::move(ID);
59506f32e7eSjoerg return *Descriptors[MCI.getOpcode()];
59606f32e7eSjoerg }
59706f32e7eSjoerg
59806f32e7eSjoerg VariantDescriptors[&MCI] = std::move(ID);
59906f32e7eSjoerg return *VariantDescriptors[&MCI];
60006f32e7eSjoerg }
60106f32e7eSjoerg
60206f32e7eSjoerg Expected<const InstrDesc &>
getOrCreateInstrDesc(const MCInst & MCI)60306f32e7eSjoerg InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
60406f32e7eSjoerg if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
60506f32e7eSjoerg return *Descriptors[MCI.getOpcode()];
60606f32e7eSjoerg
60706f32e7eSjoerg if (VariantDescriptors.find(&MCI) != VariantDescriptors.end())
60806f32e7eSjoerg return *VariantDescriptors[&MCI];
60906f32e7eSjoerg
61006f32e7eSjoerg return createInstrDescImpl(MCI);
61106f32e7eSjoerg }
61206f32e7eSjoerg
61306f32e7eSjoerg Expected<std::unique_ptr<Instruction>>
createInstruction(const MCInst & MCI)61406f32e7eSjoerg InstrBuilder::createInstruction(const MCInst &MCI) {
61506f32e7eSjoerg Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI);
61606f32e7eSjoerg if (!DescOrErr)
61706f32e7eSjoerg return DescOrErr.takeError();
61806f32e7eSjoerg const InstrDesc &D = *DescOrErr;
61906f32e7eSjoerg std::unique_ptr<Instruction> NewIS = std::make_unique<Instruction>(D);
62006f32e7eSjoerg
62106f32e7eSjoerg // Check if this is a dependency breaking instruction.
62206f32e7eSjoerg APInt Mask;
62306f32e7eSjoerg
62406f32e7eSjoerg bool IsZeroIdiom = false;
62506f32e7eSjoerg bool IsDepBreaking = false;
62606f32e7eSjoerg if (MCIA) {
62706f32e7eSjoerg unsigned ProcID = STI.getSchedModel().getProcessorID();
62806f32e7eSjoerg IsZeroIdiom = MCIA->isZeroIdiom(MCI, Mask, ProcID);
62906f32e7eSjoerg IsDepBreaking =
63006f32e7eSjoerg IsZeroIdiom || MCIA->isDependencyBreaking(MCI, Mask, ProcID);
63106f32e7eSjoerg if (MCIA->isOptimizableRegisterMove(MCI, ProcID))
63206f32e7eSjoerg NewIS->setOptimizableMove();
63306f32e7eSjoerg }
63406f32e7eSjoerg
63506f32e7eSjoerg // Initialize Reads first.
63606f32e7eSjoerg MCPhysReg RegID = 0;
63706f32e7eSjoerg for (const ReadDescriptor &RD : D.Reads) {
63806f32e7eSjoerg if (!RD.isImplicitRead()) {
63906f32e7eSjoerg // explicit read.
64006f32e7eSjoerg const MCOperand &Op = MCI.getOperand(RD.OpIndex);
64106f32e7eSjoerg // Skip non-register operands.
64206f32e7eSjoerg if (!Op.isReg())
64306f32e7eSjoerg continue;
64406f32e7eSjoerg RegID = Op.getReg();
64506f32e7eSjoerg } else {
64606f32e7eSjoerg // Implicit read.
64706f32e7eSjoerg RegID = RD.RegisterID;
64806f32e7eSjoerg }
64906f32e7eSjoerg
65006f32e7eSjoerg // Skip invalid register operands.
65106f32e7eSjoerg if (!RegID)
65206f32e7eSjoerg continue;
65306f32e7eSjoerg
65406f32e7eSjoerg // Okay, this is a register operand. Create a ReadState for it.
65506f32e7eSjoerg NewIS->getUses().emplace_back(RD, RegID);
65606f32e7eSjoerg ReadState &RS = NewIS->getUses().back();
65706f32e7eSjoerg
65806f32e7eSjoerg if (IsDepBreaking) {
65906f32e7eSjoerg // A mask of all zeroes means: explicit input operands are not
66006f32e7eSjoerg // independent.
66106f32e7eSjoerg if (Mask.isNullValue()) {
66206f32e7eSjoerg if (!RD.isImplicitRead())
66306f32e7eSjoerg RS.setIndependentFromDef();
66406f32e7eSjoerg } else {
66506f32e7eSjoerg // Check if this register operand is independent according to `Mask`.
66606f32e7eSjoerg // Note that Mask may not have enough bits to describe all explicit and
66706f32e7eSjoerg // implicit input operands. If this register operand doesn't have a
66806f32e7eSjoerg // corresponding bit in Mask, then conservatively assume that it is
66906f32e7eSjoerg // dependent.
67006f32e7eSjoerg if (Mask.getBitWidth() > RD.UseIndex) {
67106f32e7eSjoerg // Okay. This map describe register use `RD.UseIndex`.
67206f32e7eSjoerg if (Mask[RD.UseIndex])
67306f32e7eSjoerg RS.setIndependentFromDef();
67406f32e7eSjoerg }
67506f32e7eSjoerg }
67606f32e7eSjoerg }
67706f32e7eSjoerg }
67806f32e7eSjoerg
67906f32e7eSjoerg // Early exit if there are no writes.
68006f32e7eSjoerg if (D.Writes.empty())
68106f32e7eSjoerg return std::move(NewIS);
68206f32e7eSjoerg
68306f32e7eSjoerg // Track register writes that implicitly clear the upper portion of the
68406f32e7eSjoerg // underlying super-registers using an APInt.
68506f32e7eSjoerg APInt WriteMask(D.Writes.size(), 0);
68606f32e7eSjoerg
68706f32e7eSjoerg // Now query the MCInstrAnalysis object to obtain information about which
68806f32e7eSjoerg // register writes implicitly clear the upper portion of a super-register.
68906f32e7eSjoerg if (MCIA)
69006f32e7eSjoerg MCIA->clearsSuperRegisters(MRI, MCI, WriteMask);
69106f32e7eSjoerg
69206f32e7eSjoerg // Initialize writes.
69306f32e7eSjoerg unsigned WriteIndex = 0;
69406f32e7eSjoerg for (const WriteDescriptor &WD : D.Writes) {
69506f32e7eSjoerg RegID = WD.isImplicitWrite() ? WD.RegisterID
69606f32e7eSjoerg : MCI.getOperand(WD.OpIndex).getReg();
69706f32e7eSjoerg // Check if this is a optional definition that references NoReg.
69806f32e7eSjoerg if (WD.IsOptionalDef && !RegID) {
69906f32e7eSjoerg ++WriteIndex;
70006f32e7eSjoerg continue;
70106f32e7eSjoerg }
70206f32e7eSjoerg
70306f32e7eSjoerg assert(RegID && "Expected a valid register ID!");
70406f32e7eSjoerg NewIS->getDefs().emplace_back(WD, RegID,
70506f32e7eSjoerg /* ClearsSuperRegs */ WriteMask[WriteIndex],
70606f32e7eSjoerg /* WritesZero */ IsZeroIdiom);
70706f32e7eSjoerg ++WriteIndex;
70806f32e7eSjoerg }
70906f32e7eSjoerg
71006f32e7eSjoerg return std::move(NewIS);
71106f32e7eSjoerg }
71206f32e7eSjoerg } // namespace mca
71306f32e7eSjoerg } // namespace llvm
714