Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 1 | //===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | /// \file |
| 10 | /// |
| 11 | /// This file implements the InstrBuilder interface. |
| 12 | /// |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "InstrBuilder.h" |
| 16 | #include "llvm/MC/MCInst.h" |
| 17 | #include "llvm/Support/Debug.h" |
| 18 | #include "llvm/Support/raw_ostream.h" |
Andrea Di Biagio | 24fb4fc | 2018-05-04 13:52:12 +0000 | [diff] [blame] | 19 | #include "llvm/Support/WithColor.h" |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 20 | |
| 21 | #define DEBUG_TYPE "llvm-mca" |
| 22 | |
| 23 | namespace mca { |
| 24 | |
| 25 | using namespace llvm; |
| 26 | |
Andrea Di Biagio | 94fafdf | 2018-03-24 16:05:36 +0000 | [diff] [blame] | 27 | static void initializeUsedResources(InstrDesc &ID, |
| 28 | const MCSchedClassDesc &SCDesc, |
| 29 | const MCSubtargetInfo &STI, |
| 30 | ArrayRef<uint64_t> ProcResourceMasks) { |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 31 | const MCSchedModel &SM = STI.getSchedModel(); |
| 32 | |
| 33 | // Populate resources consumed. |
| 34 | using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>; |
| 35 | std::vector<ResourcePlusCycles> Worklist; |
| 36 | for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) { |
| 37 | const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I; |
| 38 | const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx); |
| 39 | uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx]; |
| 40 | if (PR.BufferSize != -1) |
| 41 | ID.Buffers.push_back(Mask); |
| 42 | CycleSegment RCy(0, PRE->Cycles, false); |
| 43 | Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy))); |
| 44 | } |
| 45 | |
| 46 | // Sort elements by mask popcount, so that we prioritize resource units over |
| 47 | // resource groups, and smaller groups over larger groups. |
Mandeep Singh Grang | 8db564e | 2018-04-01 21:24:53 +0000 | [diff] [blame] | 48 | llvm::sort(Worklist.begin(), Worklist.end(), |
| 49 | [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) { |
| 50 | unsigned popcntA = countPopulation(A.first); |
| 51 | unsigned popcntB = countPopulation(B.first); |
| 52 | if (popcntA < popcntB) |
| 53 | return true; |
| 54 | if (popcntA > popcntB) |
| 55 | return false; |
| 56 | return A.first < B.first; |
| 57 | }); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 58 | |
| 59 | uint64_t UsedResourceUnits = 0; |
| 60 | |
| 61 | // Remove cycles contributed by smaller resources. |
| 62 | for (unsigned I = 0, E = Worklist.size(); I < E; ++I) { |
| 63 | ResourcePlusCycles &A = Worklist[I]; |
| 64 | if (!A.second.size()) { |
| 65 | A.second.NumUnits = 0; |
| 66 | A.second.setReserved(); |
| 67 | ID.Resources.emplace_back(A); |
| 68 | continue; |
| 69 | } |
| 70 | |
| 71 | ID.Resources.emplace_back(A); |
| 72 | uint64_t NormalizedMask = A.first; |
| 73 | if (countPopulation(A.first) == 1) { |
| 74 | UsedResourceUnits |= A.first; |
| 75 | } else { |
| 76 | // Remove the leading 1 from the resource group mask. |
| 77 | NormalizedMask ^= PowerOf2Floor(NormalizedMask); |
| 78 | } |
| 79 | |
| 80 | for (unsigned J = I + 1; J < E; ++J) { |
| 81 | ResourcePlusCycles &B = Worklist[J]; |
| 82 | if ((NormalizedMask & B.first) == NormalizedMask) { |
| 83 | B.second.CS.Subtract(A.second.size()); |
| 84 | if (countPopulation(B.first) > 1) |
| 85 | B.second.NumUnits++; |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | // A SchedWrite may specify a number of cycles in which a resource group |
| 91 | // is reserved. For example (on target x86; cpu Haswell): |
| 92 | // |
| 93 | // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> { |
| 94 | // let ResourceCycles = [2, 2, 3]; |
| 95 | // } |
| 96 | // |
| 97 | // This means: |
| 98 | // Resource units HWPort0 and HWPort1 are both used for 2cy. |
| 99 | // Resource group HWPort01 is the union of HWPort0 and HWPort1. |
| 100 | // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01 |
| 101 | // will not be usable for 2 entire cycles from instruction issue. |
| 102 | // |
| 103 | // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency |
| 104 | // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an |
| 105 | // extra delay on top of the 2 cycles latency. |
| 106 | // During those extra cycles, HWPort01 is not usable by other instructions. |
| 107 | for (ResourcePlusCycles &RPC : ID.Resources) { |
| 108 | if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) { |
| 109 | // Remove the leading 1 from the resource group mask. |
| 110 | uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first); |
| 111 | if ((Mask & UsedResourceUnits) == Mask) |
| 112 | RPC.second.setReserved(); |
| 113 | } |
| 114 | } |
| 115 | |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 116 | LLVM_DEBUG({ |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 117 | for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources) |
| 118 | dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n'; |
| 119 | for (const uint64_t R : ID.Buffers) |
| 120 | dbgs() << "\t\tBuffer Mask=" << R << '\n'; |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 121 | }); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc, |
| 125 | const MCSchedClassDesc &SCDesc, |
| 126 | const MCSubtargetInfo &STI) { |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 127 | if (MCDesc.isCall()) { |
| 128 | // We cannot estimate how long this call will take. |
| 129 | // Artificially set an arbitrarily high latency (100cy). |
Andrea Di Biagio | c95a130 | 2018-03-13 15:59:59 +0000 | [diff] [blame] | 130 | ID.MaxLatency = 100U; |
| 131 | return; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 132 | } |
| 133 | |
Andrea Di Biagio | c95a130 | 2018-03-13 15:59:59 +0000 | [diff] [blame] | 134 | int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); |
| 135 | // If latency is unknown, then conservatively assume a MaxLatency of 100cy. |
| 136 | ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | static void populateWrites(InstrDesc &ID, const MCInst &MCI, |
| 140 | const MCInstrDesc &MCDesc, |
| 141 | const MCSchedClassDesc &SCDesc, |
| 142 | const MCSubtargetInfo &STI) { |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 143 | // Set if writes through this opcode may update super registers. |
| 144 | // TODO: on x86-64, a 4 byte write of a general purpose register always |
| 145 | // fully updates the super-register. |
| 146 | // More in general, (at least on x86) not all register writes perform |
| 147 | // a partial (super-)register update. |
| 148 | // For example, an AVX instruction that writes on a XMM register implicitly |
| 149 | // zeroes the upper half of every aliasing super-register. |
| 150 | // |
| 151 | // For now, we pessimistically assume that writes are all potentially |
| 152 | // partial register updates. This is a good default for most targets, execept |
| 153 | // for those like x86 which implement a special semantic for certain opcodes. |
| 154 | // At least on x86, this may lead to an inaccurate prediction of the |
| 155 | // instruction level parallelism. |
| 156 | bool FullyUpdatesSuperRegisters = false; |
| 157 | |
| 158 | // Now Populate Writes. |
| 159 | |
| 160 | // This algorithm currently works under the strong (and potentially incorrect) |
| 161 | // assumption that information related to register def/uses can be obtained |
| 162 | // from MCInstrDesc. |
| 163 | // |
| 164 | // However class MCInstrDesc is used to describe MachineInstr objects and not |
| 165 | // MCInst objects. To be more specific, MCInstrDesc objects are opcode |
| 166 | // descriptors that are automatically generated via tablegen based on the |
| 167 | // instruction set information available from the target .td files. That |
| 168 | // means, the number of (explicit) definitions according to MCInstrDesc always |
| 169 | // matches the cardinality of the `(outs)` set in tablegen. |
| 170 | // |
| 171 | // By constructions, definitions must appear first in the operand sequence of |
| 172 | // a MachineInstr. Also, the (outs) sequence is preserved (example: the first |
| 173 | // element in the outs set is the first operand in the corresponding |
| 174 | // MachineInstr). That's the reason why MCInstrDesc only needs to declare the |
| 175 | // total number of register definitions, and not where those definitions are |
| 176 | // in the machine operand sequence. |
| 177 | // |
| 178 | // Unfortunately, it is not safe to use the information from MCInstrDesc to |
| 179 | // also describe MCInst objects. An MCInst object can be obtained from a |
| 180 | // MachineInstr through a lowering step which may restructure the operand |
| 181 | // sequence (and even remove or introduce new operands). So, there is a high |
| 182 | // risk that the lowering step breaks the assumptions that register |
| 183 | // definitions are always at the beginning of the machine operand sequence. |
| 184 | // |
| 185 | // This is a fundamental problem, and it is still an open problem. Essentially |
| 186 | // we have to find a way to correlate def/use operands of a MachineInstr to |
| 187 | // operands of an MCInst. Otherwise, we cannot correctly reconstruct data |
| 188 | // dependencies, nor we can correctly interpret the scheduling model, which |
| 189 | // heavily uses machine operand indices to define processor read-advance |
| 190 | // information, and to identify processor write resources. Essentially, we |
| 191 | // either need something like a MCInstrDesc, but for MCInst, or a way |
| 192 | // to map MCInst operands back to MachineInstr operands. |
| 193 | // |
| 194 | // Unfortunately, we don't have that information now. So, this prototype |
| 195 | // currently work under the strong assumption that we can always safely trust |
| 196 | // the content of an MCInstrDesc. For example, we can query a MCInstrDesc to |
| 197 | // obtain the number of explicit and implicit register defintions. We also |
| 198 | // assume that register definitions always come first in the operand sequence. |
| 199 | // This last assumption usually makes sense for MachineInstr, where register |
| 200 | // definitions always appear at the beginning of the operands sequence. In |
| 201 | // reality, these assumptions could be broken by the lowering step, which can |
| 202 | // decide to lay out operands in a different order than the original order of |
| 203 | // operand as specified by the MachineInstr. |
| 204 | // |
| 205 | // Things get even more complicated in the presence of "optional" register |
| 206 | // definitions. For MachineInstr, optional register definitions are always at |
| 207 | // the end of the operand sequence. Some ARM instructions that may update the |
| 208 | // status flags specify that register as a optional operand. Since we don't |
| 209 | // have operand descriptors for MCInst, we assume for now that the optional |
| 210 | // definition is always the last operand of a MCInst. Again, this assumption |
| 211 | // may be okay for most targets. However, there is no guarantee that targets |
| 212 | // would respect that. |
| 213 | // |
| 214 | // In conclusion: these are for now the strong assumptions made by the tool: |
| 215 | // * The number of explicit and implicit register definitions in a MCInst |
| 216 | // matches the number of explicit and implicit definitions according to |
| 217 | // the opcode descriptor (MCInstrDesc). |
| 218 | // * Register definitions take precedence over register uses in the operands |
| 219 | // list. |
| 220 | // * If an opcode specifies an optional definition, then the optional |
| 221 | // definition is always the last operand in the sequence, and it can be |
| 222 | // set to zero (i.e. "no register"). |
| 223 | // |
| 224 | // These assumptions work quite well for most out-of-order in-tree targets |
| 225 | // like x86. This is mainly because the vast majority of instructions is |
| 226 | // expanded to MCInst using a straightforward lowering logic that preserves |
| 227 | // the ordering of the operands. |
| 228 | // |
| 229 | // In the longer term, we need to find a proper solution for this issue. |
| 230 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
| 231 | unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs(); |
| 232 | unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries; |
| 233 | unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs; |
| 234 | if (MCDesc.hasOptionalDef()) |
| 235 | TotalDefs++; |
| 236 | ID.Writes.resize(TotalDefs); |
| 237 | // Iterate over the operands list, and skip non-register operands. |
| 238 | // The first NumExplictDefs register operands are expected to be register |
| 239 | // definitions. |
| 240 | unsigned CurrentDef = 0; |
| 241 | unsigned i = 0; |
| 242 | for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) { |
| 243 | const MCOperand &Op = MCI.getOperand(i); |
| 244 | if (!Op.isReg()) |
| 245 | continue; |
| 246 | |
| 247 | WriteDescriptor &Write = ID.Writes[CurrentDef]; |
| 248 | Write.OpIndex = i; |
| 249 | if (CurrentDef < NumWriteLatencyEntries) { |
| 250 | const MCWriteLatencyEntry &WLE = |
| 251 | *STI.getWriteLatencyEntry(&SCDesc, CurrentDef); |
| 252 | // Conservatively default to MaxLatency. |
| 253 | Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles; |
| 254 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
| 255 | } else { |
| 256 | // Assign a default latency for this write. |
| 257 | Write.Latency = ID.MaxLatency; |
| 258 | Write.SClassOrWriteResourceID = 0; |
| 259 | } |
| 260 | Write.FullyUpdatesSuperRegs = FullyUpdatesSuperRegisters; |
| 261 | Write.IsOptionalDef = false; |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 262 | LLVM_DEBUG({ |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 263 | dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", Latency=" << Write.Latency |
| 264 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n'; |
| 265 | }); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 266 | CurrentDef++; |
| 267 | } |
| 268 | |
| 269 | if (CurrentDef != NumExplicitDefs) |
| 270 | llvm::report_fatal_error( |
| 271 | "error: Expected more register operand definitions. "); |
| 272 | |
| 273 | CurrentDef = 0; |
| 274 | for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) { |
| 275 | unsigned Index = NumExplicitDefs + CurrentDef; |
| 276 | WriteDescriptor &Write = ID.Writes[Index]; |
| 277 | Write.OpIndex = -1; |
| 278 | Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef]; |
Andrea Di Biagio | 6fd62fe | 2018-04-02 13:46:49 +0000 | [diff] [blame] | 279 | if (Index < NumWriteLatencyEntries) { |
| 280 | const MCWriteLatencyEntry &WLE = |
| 281 | *STI.getWriteLatencyEntry(&SCDesc, Index); |
| 282 | // Conservatively default to MaxLatency. |
| 283 | Write.Latency = WLE.Cycles == -1 ? ID.MaxLatency : WLE.Cycles; |
| 284 | Write.SClassOrWriteResourceID = WLE.WriteResourceID; |
| 285 | } else { |
| 286 | // Assign a default latency for this write. |
| 287 | Write.Latency = ID.MaxLatency; |
| 288 | Write.SClassOrWriteResourceID = 0; |
| 289 | } |
| 290 | |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 291 | Write.IsOptionalDef = false; |
| 292 | assert(Write.RegisterID != 0 && "Expected a valid phys register!"); |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 293 | LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Write.OpIndex << ", PhysReg=" |
| 294 | << Write.RegisterID << ", Latency=" << Write.Latency |
| 295 | << ", WriteResourceID=" << Write.SClassOrWriteResourceID |
| 296 | << '\n'); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | if (MCDesc.hasOptionalDef()) { |
| 300 | // Always assume that the optional definition is the last operand of the |
| 301 | // MCInst sequence. |
| 302 | const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1); |
| 303 | if (i == MCI.getNumOperands() || !Op.isReg()) |
| 304 | llvm::report_fatal_error( |
| 305 | "error: expected a register operand for an optional " |
| 306 | "definition. Instruction has not be correctly analyzed.\n", |
| 307 | false); |
| 308 | |
| 309 | WriteDescriptor &Write = ID.Writes[TotalDefs - 1]; |
| 310 | Write.OpIndex = MCI.getNumOperands() - 1; |
| 311 | // Assign a default latency for this write. |
| 312 | Write.Latency = ID.MaxLatency; |
| 313 | Write.SClassOrWriteResourceID = 0; |
| 314 | Write.IsOptionalDef = true; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | static void populateReads(InstrDesc &ID, const MCInst &MCI, |
| 319 | const MCInstrDesc &MCDesc, |
| 320 | const MCSchedClassDesc &SCDesc, |
| 321 | const MCSubtargetInfo &STI) { |
| 322 | unsigned SchedClassID = MCDesc.getSchedClass(); |
| 323 | bool HasReadAdvanceEntries = SCDesc.NumReadAdvanceEntries > 0; |
| 324 | |
| 325 | unsigned i = 0; |
| 326 | unsigned NumExplicitDefs = MCDesc.getNumDefs(); |
| 327 | // Skip explicit definitions. |
| 328 | for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) { |
| 329 | const MCOperand &Op = MCI.getOperand(i); |
| 330 | if (Op.isReg()) |
| 331 | NumExplicitDefs--; |
| 332 | } |
| 333 | |
| 334 | if (NumExplicitDefs) |
| 335 | llvm::report_fatal_error( |
| 336 | "error: Expected more register operand definitions. ", false); |
| 337 | |
| 338 | unsigned NumExplicitUses = MCI.getNumOperands() - i; |
| 339 | unsigned NumImplicitUses = MCDesc.getNumImplicitUses(); |
| 340 | if (MCDesc.hasOptionalDef()) { |
| 341 | assert(NumExplicitUses); |
| 342 | NumExplicitUses--; |
| 343 | } |
| 344 | unsigned TotalUses = NumExplicitUses + NumImplicitUses; |
| 345 | if (!TotalUses) |
| 346 | return; |
| 347 | |
| 348 | ID.Reads.resize(TotalUses); |
| 349 | for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) { |
| 350 | ReadDescriptor &Read = ID.Reads[CurrentUse]; |
| 351 | Read.OpIndex = i + CurrentUse; |
Andrea Di Biagio | 0a837ef | 2018-03-29 14:26:56 +0000 | [diff] [blame] | 352 | Read.UseIndex = CurrentUse; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 353 | Read.HasReadAdvanceEntries = HasReadAdvanceEntries; |
| 354 | Read.SchedClassID = SchedClassID; |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 355 | LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndex); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) { |
| 359 | ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse]; |
| 360 | Read.OpIndex = -1; |
Andrea Di Biagio | 6fd62fe | 2018-04-02 13:46:49 +0000 | [diff] [blame] | 361 | Read.UseIndex = NumExplicitUses + CurrentUse; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 362 | Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse]; |
Andrea Di Biagio | 6fd62fe | 2018-04-02 13:46:49 +0000 | [diff] [blame] | 363 | Read.HasReadAdvanceEntries = HasReadAdvanceEntries; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 364 | Read.SchedClassID = SchedClassID; |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 365 | LLVM_DEBUG(dbgs() << "\t\tOpIdx=" << Read.OpIndex |
| 366 | << ", RegisterID=" << Read.RegisterID << '\n'); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 367 | } |
| 368 | } |
| 369 | |
Andrea Di Biagio | 49c8591 | 2018-05-04 13:10:10 +0000 | [diff] [blame] | 370 | const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) { |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 371 | assert(STI.getSchedModel().hasInstrSchedModel() && |
| 372 | "Itineraries are not yet supported!"); |
| 373 | |
| 374 | unsigned short Opcode = MCI.getOpcode(); |
| 375 | // Obtain the instruction descriptor from the opcode. |
| 376 | const MCInstrDesc &MCDesc = MCII.get(Opcode); |
| 377 | const MCSchedModel &SM = STI.getSchedModel(); |
| 378 | |
| 379 | // Then obtain the scheduling class information from the instruction. |
Andrea Di Biagio | 49c8591 | 2018-05-04 13:10:10 +0000 | [diff] [blame] | 380 | unsigned SchedClassID = MCDesc.getSchedClass(); |
| 381 | const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 382 | |
| 383 | // Create a new empty descriptor. |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 384 | std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>(); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 385 | |
| 386 | if (SCDesc.isVariant()) { |
Andrea Di Biagio | 24fb4fc | 2018-05-04 13:52:12 +0000 | [diff] [blame] | 387 | WithColor::warning() << "don't know how to model variant opcodes.\n"; |
| 388 | WithColor::note() << "assume 1 micro opcode.\n"; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 389 | ID->NumMicroOps = 1U; |
| 390 | } else { |
| 391 | ID->NumMicroOps = SCDesc.NumMicroOps; |
| 392 | } |
| 393 | |
| 394 | if (MCDesc.isCall()) { |
| 395 | // We don't correctly model calls. |
Andrea Di Biagio | 24fb4fc | 2018-05-04 13:52:12 +0000 | [diff] [blame] | 396 | WithColor::warning() << "found a call in the input assembly sequence.\n"; |
| 397 | WithColor::note() << "call instructions are not correctly modeled. " |
| 398 | << "Assume a latency of 100cy.\n"; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 399 | } |
| 400 | |
| 401 | if (MCDesc.isReturn()) { |
Andrea Di Biagio | 24fb4fc | 2018-05-04 13:52:12 +0000 | [diff] [blame] | 402 | WithColor::warning() << "found a return instruction in the input" |
| 403 | << " assembly sequence.\n"; |
| 404 | WithColor::note() << "program counter updates are ignored.\n"; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 405 | } |
| 406 | |
| 407 | ID->MayLoad = MCDesc.mayLoad(); |
| 408 | ID->MayStore = MCDesc.mayStore(); |
| 409 | ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects(); |
| 410 | |
| 411 | initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks); |
Andrea Di Biagio | db66efc | 2018-04-25 09:38:58 +0000 | [diff] [blame] | 412 | computeMaxLatency(*ID, MCDesc, SCDesc, STI); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 413 | populateWrites(*ID, MCI, MCDesc, SCDesc, STI); |
| 414 | populateReads(*ID, MCI, MCDesc, SCDesc, STI); |
| 415 | |
Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame^] | 416 | LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n'); |
| 417 | LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n'); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 418 | |
| 419 | // Now add the new descriptor. |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 420 | Descriptors[Opcode] = std::move(ID); |
Andrea Di Biagio | 49c8591 | 2018-05-04 13:10:10 +0000 | [diff] [blame] | 421 | return *Descriptors[Opcode]; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 422 | } |
| 423 | |
Andrea Di Biagio | 4704f03 | 2018-03-20 12:25:54 +0000 | [diff] [blame] | 424 | const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { |
Andrea Di Biagio | 3562248 | 2018-03-22 10:19:20 +0000 | [diff] [blame] | 425 | if (Descriptors.find_as(MCI.getOpcode()) == Descriptors.end()) |
Andrea Di Biagio | 49c8591 | 2018-05-04 13:10:10 +0000 | [diff] [blame] | 426 | return createInstrDescImpl(MCI); |
Andrea Di Biagio | 3562248 | 2018-03-22 10:19:20 +0000 | [diff] [blame] | 427 | return *Descriptors[MCI.getOpcode()]; |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 428 | } |
| 429 | |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 430 | std::unique_ptr<Instruction> |
Andrea Di Biagio | 49c8591 | 2018-05-04 13:10:10 +0000 | [diff] [blame] | 431 | InstrBuilder::createInstruction(const MCInst &MCI) { |
Andrea Di Biagio | 4704f03 | 2018-03-20 12:25:54 +0000 | [diff] [blame] | 432 | const InstrDesc &D = getOrCreateInstrDesc(MCI); |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 433 | std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 434 | |
Andrea Di Biagio | db66efc | 2018-04-25 09:38:58 +0000 | [diff] [blame] | 435 | // Initialize Reads first. |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 436 | for (const ReadDescriptor &RD : D.Reads) { |
| 437 | int RegID = -1; |
| 438 | if (RD.OpIndex != -1) { |
| 439 | // explicit read. |
| 440 | const MCOperand &Op = MCI.getOperand(RD.OpIndex); |
| 441 | // Skip non-register operands. |
| 442 | if (!Op.isReg()) |
| 443 | continue; |
| 444 | RegID = Op.getReg(); |
| 445 | } else { |
| 446 | // Implicit read. |
| 447 | RegID = RD.RegisterID; |
| 448 | } |
| 449 | |
| 450 | // Skip invalid register operands. |
| 451 | if (!RegID) |
| 452 | continue; |
| 453 | |
| 454 | // Okay, this is a register operand. Create a ReadState for it. |
| 455 | assert(RegID > 0 && "Invalid register ID found!"); |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 456 | NewIS->getUses().emplace_back(llvm::make_unique<ReadState>(RD, RegID)); |
Andrea Di Biagio | 4704f03 | 2018-03-20 12:25:54 +0000 | [diff] [blame] | 457 | } |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 458 | |
Andrea Di Biagio | db66efc | 2018-04-25 09:38:58 +0000 | [diff] [blame] | 459 | // Initialize writes. |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 460 | for (const WriteDescriptor &WD : D.Writes) { |
| 461 | unsigned RegID = |
| 462 | WD.OpIndex == -1 ? WD.RegisterID : MCI.getOperand(WD.OpIndex).getReg(); |
Andrea Di Biagio | 3562248 | 2018-03-22 10:19:20 +0000 | [diff] [blame] | 463 | // Check if this is a optional definition that references NoReg. |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 464 | if (WD.IsOptionalDef && !RegID) |
| 465 | continue; |
| 466 | |
Andrea Di Biagio | 3562248 | 2018-03-22 10:19:20 +0000 | [diff] [blame] | 467 | assert(RegID && "Expected a valid register ID!"); |
Andrea Di Biagio | 7b3d162 | 2018-03-20 12:58:34 +0000 | [diff] [blame] | 468 | NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(WD, RegID)); |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 469 | } |
| 470 | |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 471 | return NewIS; |
| 472 | } |
Andrea Di Biagio | 3a6b092 | 2018-03-08 13:05:02 +0000 | [diff] [blame] | 473 | } // namespace mca |