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