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