1 | //===- SubtargetEmitter.cpp - Generate subtarget enumerations -------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This tablegen backend emits subtarget enumerations. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "Common/CodeGenHwModes.h" |
14 | #include "Common/CodeGenSchedule.h" |
15 | #include "Common/CodeGenTarget.h" |
16 | #include "Common/PredicateExpander.h" |
17 | #include "llvm/ADT/STLExtras.h" |
18 | #include "llvm/ADT/SmallPtrSet.h" |
19 | #include "llvm/ADT/StringExtras.h" |
20 | #include "llvm/ADT/StringRef.h" |
21 | #include "llvm/MC/MCInstrItineraries.h" |
22 | #include "llvm/MC/MCSchedule.h" |
23 | #include "llvm/Support/Debug.h" |
24 | #include "llvm/Support/Format.h" |
25 | #include "llvm/Support/raw_ostream.h" |
26 | #include "llvm/TableGen/Error.h" |
27 | #include "llvm/TableGen/Record.h" |
28 | #include "llvm/TableGen/TableGenBackend.h" |
29 | #include "llvm/TargetParser/SubtargetFeature.h" |
30 | #include <algorithm> |
31 | #include <cassert> |
32 | #include <cstdint> |
33 | #include <iterator> |
34 | #include <map> |
35 | #include <string> |
36 | #include <vector> |
37 | |
38 | using namespace llvm; |
39 | |
40 | #define DEBUG_TYPE "subtarget-emitter" |
41 | |
42 | namespace { |
43 | |
44 | /// Sorting predicate to sort record pointers by their |
45 | /// FieldName field. |
46 | struct LessRecordFieldFieldName { |
47 | bool operator()(const Record *Rec1, const Record *Rec2) const { |
48 | return Rec1->getValueAsString(FieldName: "FieldName" ) < |
49 | Rec2->getValueAsString(FieldName: "FieldName" ); |
50 | } |
51 | }; |
52 | |
53 | class SubtargetEmitter { |
54 | // Each processor has a SchedClassDesc table with an entry for each |
55 | // SchedClass. The SchedClassDesc table indexes into a global write resource |
56 | // table, write latency table, and read advance table. |
57 | struct SchedClassTables { |
58 | std::vector<std::vector<MCSchedClassDesc>> ProcSchedClasses; |
59 | std::vector<MCWriteProcResEntry> WriteProcResources; |
60 | std::vector<MCWriteLatencyEntry> WriteLatencies; |
61 | std::vector<std::string> WriterNames; |
62 | std::vector<MCReadAdvanceEntry> ReadAdvanceEntries; |
63 | |
64 | // Reserve an invalid entry at index 0 |
65 | SchedClassTables() { |
66 | ProcSchedClasses.resize(new_size: 1); |
67 | WriteProcResources.resize(new_size: 1); |
68 | WriteLatencies.resize(new_size: 1); |
69 | WriterNames.push_back(x: "InvalidWrite" ); |
70 | ReadAdvanceEntries.resize(new_size: 1); |
71 | } |
72 | }; |
73 | |
74 | struct LessWriteProcResources { |
75 | bool operator()(const MCWriteProcResEntry &LHS, |
76 | const MCWriteProcResEntry &RHS) { |
77 | return LHS.ProcResourceIdx < RHS.ProcResourceIdx; |
78 | } |
79 | }; |
80 | |
81 | CodeGenTarget TGT; |
82 | RecordKeeper &Records; |
83 | CodeGenSchedModels &SchedModels; |
84 | std::string Target; |
85 | |
86 | void Enumeration(raw_ostream &OS, DenseMap<Record *, unsigned> &FeatureMap); |
87 | void EmitSubtargetInfoMacroCalls(raw_ostream &OS); |
88 | unsigned FeatureKeyValues(raw_ostream &OS, |
89 | const DenseMap<Record *, unsigned> &FeatureMap); |
90 | unsigned CPUKeyValues(raw_ostream &OS, |
91 | const DenseMap<Record *, unsigned> &FeatureMap); |
92 | void FormItineraryStageString(const std::string &Names, Record *ItinData, |
93 | std::string &ItinString, unsigned &NStages); |
94 | void FormItineraryOperandCycleString(Record *ItinData, |
95 | std::string &ItinString, |
96 | unsigned &NOperandCycles); |
97 | void FormItineraryBypassString(const std::string &Names, Record *ItinData, |
98 | std::string &ItinString, |
99 | unsigned NOperandCycles); |
100 | void EmitStageAndOperandCycleData( |
101 | raw_ostream &OS, std::vector<std::vector<InstrItinerary>> &ProcItinLists); |
102 | void EmitItineraries(raw_ostream &OS, |
103 | std::vector<std::vector<InstrItinerary>> &ProcItinLists); |
104 | unsigned EmitRegisterFileTables(const CodeGenProcModel &ProcModel, |
105 | raw_ostream &OS); |
106 | void EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel, |
107 | raw_ostream &OS); |
108 | void EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel, |
109 | raw_ostream &OS); |
110 | void EmitProcessorProp(raw_ostream &OS, const Record *R, StringRef Name, |
111 | char Separator); |
112 | void EmitProcessorResourceSubUnits(const CodeGenProcModel &ProcModel, |
113 | raw_ostream &OS); |
114 | void EmitProcessorResources(const CodeGenProcModel &ProcModel, |
115 | raw_ostream &OS); |
116 | Record *FindWriteResources(const CodeGenSchedRW &SchedWrite, |
117 | const CodeGenProcModel &ProcModel); |
118 | Record *FindReadAdvance(const CodeGenSchedRW &SchedRead, |
119 | const CodeGenProcModel &ProcModel); |
120 | void ExpandProcResources(RecVec &PRVec, std::vector<int64_t> &ReleaseAtCycles, |
121 | std::vector<int64_t> &AcquireAtCycles, |
122 | const CodeGenProcModel &ProcModel); |
123 | void GenSchedClassTables(const CodeGenProcModel &ProcModel, |
124 | SchedClassTables &SchedTables); |
125 | void EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS); |
126 | void EmitProcessorModels(raw_ostream &OS); |
127 | void EmitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS); |
128 | void emitSchedModelHelpersImpl(raw_ostream &OS, |
129 | bool OnlyExpandMCInstPredicates = false); |
130 | void emitGenMCSubtargetInfo(raw_ostream &OS); |
131 | void EmitMCInstrAnalysisPredicateFunctions(raw_ostream &OS); |
132 | |
133 | void EmitSchedModel(raw_ostream &OS); |
134 | void emitGetMacroFusions(const std::string &ClassName, raw_ostream &OS); |
135 | void EmitHwModeCheck(const std::string &ClassName, raw_ostream &OS); |
136 | void ParseFeaturesFunction(raw_ostream &OS); |
137 | |
138 | public: |
139 | SubtargetEmitter(RecordKeeper &R) |
140 | : TGT(R), Records(R), SchedModels(TGT.getSchedModels()), |
141 | Target(TGT.getName()) {} |
142 | |
143 | void run(raw_ostream &o); |
144 | }; |
145 | |
146 | } // end anonymous namespace |
147 | |
148 | // |
149 | // Enumeration - Emit the specified class as an enumeration. |
150 | // |
151 | void SubtargetEmitter::Enumeration(raw_ostream &OS, |
152 | DenseMap<Record *, unsigned> &FeatureMap) { |
153 | // Get all records of class and sort |
154 | std::vector<Record *> DefList = |
155 | Records.getAllDerivedDefinitions(ClassName: "SubtargetFeature" ); |
156 | llvm::sort(C&: DefList, Comp: LessRecord()); |
157 | |
158 | unsigned N = DefList.size(); |
159 | if (N == 0) |
160 | return; |
161 | if (N + 1 > MAX_SUBTARGET_FEATURES) |
162 | PrintFatalError( |
163 | Msg: "Too many subtarget features! Bump MAX_SUBTARGET_FEATURES." ); |
164 | |
165 | OS << "namespace " << Target << " {\n" ; |
166 | |
167 | // Open enumeration. |
168 | OS << "enum {\n" ; |
169 | |
170 | // For each record |
171 | for (unsigned i = 0; i < N; ++i) { |
172 | // Next record |
173 | Record *Def = DefList[i]; |
174 | |
175 | // Get and emit name |
176 | OS << " " << Def->getName() << " = " << i << ",\n" ; |
177 | |
178 | // Save the index for this feature. |
179 | FeatureMap[Def] = i; |
180 | } |
181 | |
182 | OS << " " |
183 | << "NumSubtargetFeatures = " << N << "\n" ; |
184 | |
185 | // Close enumeration and namespace |
186 | OS << "};\n" ; |
187 | OS << "} // end namespace " << Target << "\n" ; |
188 | } |
189 | |
190 | static void printFeatureMask(raw_ostream &OS, RecVec &FeatureList, |
191 | const DenseMap<Record *, unsigned> &FeatureMap) { |
192 | std::array<uint64_t, MAX_SUBTARGET_WORDS> Mask = {}; |
193 | for (const Record *Feature : FeatureList) { |
194 | unsigned Bit = FeatureMap.lookup(Val: Feature); |
195 | Mask[Bit / 64] |= 1ULL << (Bit % 64); |
196 | } |
197 | |
198 | OS << "{ { { " ; |
199 | for (unsigned i = 0; i != Mask.size(); ++i) { |
200 | OS << "0x" ; |
201 | OS.write_hex(N: Mask[i]); |
202 | OS << "ULL, " ; |
203 | } |
204 | OS << "} } }" ; |
205 | } |
206 | |
207 | /// Emit some information about the SubtargetFeature as calls to a macro so |
208 | /// that they can be used from C++. |
209 | void SubtargetEmitter::EmitSubtargetInfoMacroCalls(raw_ostream &OS) { |
210 | OS << "\n#ifdef GET_SUBTARGETINFO_MACRO\n" ; |
211 | |
212 | std::vector<Record *> FeatureList = |
213 | Records.getAllDerivedDefinitions(ClassName: "SubtargetFeature" ); |
214 | llvm::sort(C&: FeatureList, Comp: LessRecordFieldFieldName()); |
215 | |
216 | for (const Record *Feature : FeatureList) { |
217 | const StringRef FieldName = Feature->getValueAsString(FieldName: "FieldName" ); |
218 | const StringRef Value = Feature->getValueAsString(FieldName: "Value" ); |
219 | |
220 | // Only handle boolean features for now, excluding BitVectors and enums. |
221 | const bool IsBool = (Value == "false" || Value == "true" ) && |
222 | !StringRef(FieldName).contains(C: '['); |
223 | if (!IsBool) |
224 | continue; |
225 | |
226 | // Some features default to true, with values set to false if enabled. |
227 | const char *Default = Value == "false" ? "true" : "false" ; |
228 | |
229 | // Define the getter with lowercased first char: xxxYyy() { return XxxYyy; } |
230 | const std::string Getter = |
231 | FieldName.substr(Start: 0, N: 1).lower() + FieldName.substr(Start: 1).str(); |
232 | |
233 | OS << "GET_SUBTARGETINFO_MACRO(" << FieldName << ", " << Default << ", " |
234 | << Getter << ")\n" ; |
235 | } |
236 | OS << "#undef GET_SUBTARGETINFO_MACRO\n" ; |
237 | OS << "#endif // GET_SUBTARGETINFO_MACRO\n\n" ; |
238 | |
239 | OS << "\n#ifdef GET_SUBTARGETINFO_MC_DESC\n" ; |
240 | OS << "#undef GET_SUBTARGETINFO_MC_DESC\n\n" ; |
241 | } |
242 | |
243 | // |
244 | // FeatureKeyValues - Emit data of all the subtarget features. Used by the |
245 | // command line. |
246 | // |
247 | unsigned SubtargetEmitter::FeatureKeyValues( |
248 | raw_ostream &OS, const DenseMap<Record *, unsigned> &FeatureMap) { |
249 | // Gather and sort all the features |
250 | std::vector<Record *> FeatureList = |
251 | Records.getAllDerivedDefinitions(ClassName: "SubtargetFeature" ); |
252 | |
253 | if (FeatureList.empty()) |
254 | return 0; |
255 | |
256 | llvm::sort(C&: FeatureList, Comp: LessRecordFieldName()); |
257 | |
258 | // Begin feature table |
259 | OS << "// Sorted (by key) array of values for CPU features.\n" |
260 | << "extern const llvm::SubtargetFeatureKV " << Target |
261 | << "FeatureKV[] = {\n" ; |
262 | |
263 | // For each feature |
264 | unsigned NumFeatures = 0; |
265 | for (const Record *Feature : FeatureList) { |
266 | // Next feature |
267 | StringRef Name = Feature->getName(); |
268 | StringRef CommandLineName = Feature->getValueAsString(FieldName: "Name" ); |
269 | StringRef Desc = Feature->getValueAsString(FieldName: "Desc" ); |
270 | |
271 | if (CommandLineName.empty()) |
272 | continue; |
273 | |
274 | // Emit as { "feature", "description", { featureEnum }, { i1 , i2 , ... , in |
275 | // } } |
276 | OS << " { " |
277 | << "\"" << CommandLineName << "\", " |
278 | << "\"" << Desc << "\", " << Target << "::" << Name << ", " ; |
279 | |
280 | RecVec ImpliesList = Feature->getValueAsListOfDefs(FieldName: "Implies" ); |
281 | |
282 | printFeatureMask(OS, FeatureList&: ImpliesList, FeatureMap); |
283 | |
284 | OS << " },\n" ; |
285 | ++NumFeatures; |
286 | } |
287 | |
288 | // End feature table |
289 | OS << "};\n" ; |
290 | |
291 | return NumFeatures; |
292 | } |
293 | |
294 | // |
295 | // CPUKeyValues - Emit data of all the subtarget processors. Used by command |
296 | // line. |
297 | // |
298 | unsigned |
299 | SubtargetEmitter::CPUKeyValues(raw_ostream &OS, |
300 | const DenseMap<Record *, unsigned> &FeatureMap) { |
301 | // Gather and sort processor information |
302 | std::vector<Record *> ProcessorList = |
303 | Records.getAllDerivedDefinitions(ClassName: "Processor" ); |
304 | llvm::sort(C&: ProcessorList, Comp: LessRecordFieldName()); |
305 | |
306 | // Begin processor table |
307 | OS << "// Sorted (by key) array of values for CPU subtype.\n" |
308 | << "extern const llvm::SubtargetSubTypeKV " << Target |
309 | << "SubTypeKV[] = {\n" ; |
310 | |
311 | // For each processor |
312 | for (Record *Processor : ProcessorList) { |
313 | StringRef Name = Processor->getValueAsString(FieldName: "Name" ); |
314 | RecVec FeatureList = Processor->getValueAsListOfDefs(FieldName: "Features" ); |
315 | RecVec TuneFeatureList = Processor->getValueAsListOfDefs(FieldName: "TuneFeatures" ); |
316 | |
317 | // Emit as { "cpu", "description", 0, { f1 , f2 , ... fn } }, |
318 | OS << " { " |
319 | << "\"" << Name << "\", " ; |
320 | |
321 | printFeatureMask(OS, FeatureList, FeatureMap); |
322 | OS << ", " ; |
323 | printFeatureMask(OS, FeatureList&: TuneFeatureList, FeatureMap); |
324 | |
325 | // Emit the scheduler model pointer. |
326 | const std::string &ProcModelName = |
327 | SchedModels.getModelForProc(ProcDef: Processor).ModelName; |
328 | OS << ", &" << ProcModelName << " },\n" ; |
329 | } |
330 | |
331 | // End processor table |
332 | OS << "};\n" ; |
333 | |
334 | return ProcessorList.size(); |
335 | } |
336 | |
337 | // |
338 | // FormItineraryStageString - Compose a string containing the stage |
339 | // data initialization for the specified itinerary. N is the number |
340 | // of stages. |
341 | // |
342 | void SubtargetEmitter::FormItineraryStageString(const std::string &Name, |
343 | Record *ItinData, |
344 | std::string &ItinString, |
345 | unsigned &NStages) { |
346 | // Get states list |
347 | RecVec StageList = ItinData->getValueAsListOfDefs(FieldName: "Stages" ); |
348 | |
349 | // For each stage |
350 | unsigned N = NStages = StageList.size(); |
351 | for (unsigned i = 0; i < N;) { |
352 | // Next stage |
353 | const Record *Stage = StageList[i]; |
354 | |
355 | // Form string as ,{ cycles, u1 | u2 | ... | un, timeinc, kind } |
356 | int Cycles = Stage->getValueAsInt(FieldName: "Cycles" ); |
357 | ItinString += " { " + itostr(X: Cycles) + ", " ; |
358 | |
359 | // Get unit list |
360 | RecVec UnitList = Stage->getValueAsListOfDefs(FieldName: "Units" ); |
361 | |
362 | // For each unit |
363 | for (unsigned j = 0, M = UnitList.size(); j < M;) { |
364 | // Add name and bitwise or |
365 | ItinString += Name + "FU::" + UnitList[j]->getName().str(); |
366 | if (++j < M) |
367 | ItinString += " | " ; |
368 | } |
369 | |
370 | int TimeInc = Stage->getValueAsInt(FieldName: "TimeInc" ); |
371 | ItinString += ", " + itostr(X: TimeInc); |
372 | |
373 | int Kind = Stage->getValueAsInt(FieldName: "Kind" ); |
374 | ItinString += ", (llvm::InstrStage::ReservationKinds)" + itostr(X: Kind); |
375 | |
376 | // Close off stage |
377 | ItinString += " }" ; |
378 | if (++i < N) |
379 | ItinString += ", " ; |
380 | } |
381 | } |
382 | |
383 | // |
384 | // FormItineraryOperandCycleString - Compose a string containing the |
385 | // operand cycle initialization for the specified itinerary. N is the |
386 | // number of operands that has cycles specified. |
387 | // |
388 | void SubtargetEmitter::FormItineraryOperandCycleString( |
389 | Record *ItinData, std::string &ItinString, unsigned &NOperandCycles) { |
390 | // Get operand cycle list |
391 | std::vector<int64_t> OperandCycleList = |
392 | ItinData->getValueAsListOfInts(FieldName: "OperandCycles" ); |
393 | |
394 | // For each operand cycle |
395 | NOperandCycles = OperandCycleList.size(); |
396 | ListSeparator LS; |
397 | for (int OCycle : OperandCycleList) { |
398 | // Next operand cycle |
399 | ItinString += LS; |
400 | ItinString += " " + itostr(X: OCycle); |
401 | } |
402 | } |
403 | |
404 | void SubtargetEmitter::FormItineraryBypassString(const std::string &Name, |
405 | Record *ItinData, |
406 | std::string &ItinString, |
407 | unsigned NOperandCycles) { |
408 | RecVec BypassList = ItinData->getValueAsListOfDefs(FieldName: "Bypasses" ); |
409 | unsigned N = BypassList.size(); |
410 | unsigned i = 0; |
411 | ListSeparator LS; |
412 | for (; i < N; ++i) { |
413 | ItinString += LS; |
414 | ItinString += Name + "Bypass::" + BypassList[i]->getName().str(); |
415 | } |
416 | for (; i < NOperandCycles; ++i) { |
417 | ItinString += LS; |
418 | ItinString += " 0" ; |
419 | } |
420 | } |
421 | |
422 | // |
423 | // EmitStageAndOperandCycleData - Generate unique itinerary stages and operand |
424 | // cycle tables. Create a list of InstrItinerary objects (ProcItinLists) indexed |
425 | // by CodeGenSchedClass::Index. |
426 | // |
427 | void SubtargetEmitter::EmitStageAndOperandCycleData( |
428 | raw_ostream &OS, std::vector<std::vector<InstrItinerary>> &ProcItinLists) { |
429 | // Multiple processor models may share an itinerary record. Emit it once. |
430 | SmallPtrSet<Record *, 8> ItinsDefSet; |
431 | |
432 | // Emit functional units for all the itineraries. |
433 | for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { |
434 | |
435 | if (!ItinsDefSet.insert(Ptr: ProcModel.ItinsDef).second) |
436 | continue; |
437 | |
438 | RecVec FUs = ProcModel.ItinsDef->getValueAsListOfDefs(FieldName: "FU" ); |
439 | if (FUs.empty()) |
440 | continue; |
441 | |
442 | StringRef Name = ProcModel.ItinsDef->getName(); |
443 | OS << "\n// Functional units for \"" << Name << "\"\n" |
444 | << "namespace " << Name << "FU {\n" ; |
445 | |
446 | for (unsigned j = 0, FUN = FUs.size(); j < FUN; ++j) |
447 | OS << " const InstrStage::FuncUnits " << FUs[j]->getName() |
448 | << " = 1ULL << " << j << ";\n" ; |
449 | |
450 | OS << "} // end namespace " << Name << "FU\n" ; |
451 | |
452 | RecVec BPs = ProcModel.ItinsDef->getValueAsListOfDefs(FieldName: "BP" ); |
453 | if (!BPs.empty()) { |
454 | OS << "\n// Pipeline forwarding paths for itineraries \"" << Name |
455 | << "\"\n" |
456 | << "namespace " << Name << "Bypass {\n" ; |
457 | |
458 | OS << " const unsigned NoBypass = 0;\n" ; |
459 | for (unsigned j = 0, BPN = BPs.size(); j < BPN; ++j) |
460 | OS << " const unsigned " << BPs[j]->getName() << " = 1 << " << j |
461 | << ";\n" ; |
462 | |
463 | OS << "} // end namespace " << Name << "Bypass\n" ; |
464 | } |
465 | } |
466 | |
467 | // Begin stages table |
468 | std::string StageTable = |
469 | "\nextern const llvm::InstrStage " + Target + "Stages[] = {\n" ; |
470 | StageTable += " { 0, 0, 0, llvm::InstrStage::Required }, // No itinerary\n" ; |
471 | |
472 | // Begin operand cycle table |
473 | std::string OperandCycleTable = |
474 | "extern const unsigned " + Target + "OperandCycles[] = {\n" ; |
475 | OperandCycleTable += " 0, // No itinerary\n" ; |
476 | |
477 | // Begin pipeline bypass table |
478 | std::string BypassTable = |
479 | "extern const unsigned " + Target + "ForwardingPaths[] = {\n" ; |
480 | BypassTable += " 0, // No itinerary\n" ; |
481 | |
482 | // For each Itinerary across all processors, add a unique entry to the stages, |
483 | // operand cycles, and pipeline bypass tables. Then add the new Itinerary |
484 | // object with computed offsets to the ProcItinLists result. |
485 | unsigned StageCount = 1, OperandCycleCount = 1; |
486 | std::map<std::string, unsigned> ItinStageMap, ItinOperandMap; |
487 | for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { |
488 | // Add process itinerary to the list. |
489 | std::vector<InstrItinerary> &ItinList = ProcItinLists.emplace_back(); |
490 | |
491 | // If this processor defines no itineraries, then leave the itinerary list |
492 | // empty. |
493 | if (!ProcModel.hasItineraries()) |
494 | continue; |
495 | |
496 | StringRef Name = ProcModel.ItinsDef->getName(); |
497 | |
498 | ItinList.resize(new_size: SchedModels.numInstrSchedClasses()); |
499 | assert(ProcModel.ItinDefList.size() == ItinList.size() && "bad Itins" ); |
500 | |
501 | for (unsigned SchedClassIdx = 0, SchedClassEnd = ItinList.size(); |
502 | SchedClassIdx < SchedClassEnd; ++SchedClassIdx) { |
503 | |
504 | // Next itinerary data |
505 | Record *ItinData = ProcModel.ItinDefList[SchedClassIdx]; |
506 | |
507 | // Get string and stage count |
508 | std::string ItinStageString; |
509 | unsigned NStages = 0; |
510 | if (ItinData) |
511 | FormItineraryStageString(Name: std::string(Name), ItinData, ItinString&: ItinStageString, |
512 | NStages); |
513 | |
514 | // Get string and operand cycle count |
515 | std::string ItinOperandCycleString; |
516 | unsigned NOperandCycles = 0; |
517 | std::string ItinBypassString; |
518 | if (ItinData) { |
519 | FormItineraryOperandCycleString(ItinData, ItinString&: ItinOperandCycleString, |
520 | NOperandCycles); |
521 | |
522 | FormItineraryBypassString(Name: std::string(Name), ItinData, ItinString&: ItinBypassString, |
523 | NOperandCycles); |
524 | } |
525 | |
526 | // Check to see if stage already exists and create if it doesn't |
527 | uint16_t FindStage = 0; |
528 | if (NStages > 0) { |
529 | FindStage = ItinStageMap[ItinStageString]; |
530 | if (FindStage == 0) { |
531 | // Emit as { cycles, u1 | u2 | ... | un, timeinc }, // indices |
532 | StageTable += ItinStageString + ", // " + itostr(X: StageCount); |
533 | if (NStages > 1) |
534 | StageTable += "-" + itostr(X: StageCount + NStages - 1); |
535 | StageTable += "\n" ; |
536 | // Record Itin class number. |
537 | ItinStageMap[ItinStageString] = FindStage = StageCount; |
538 | StageCount += NStages; |
539 | } |
540 | } |
541 | |
542 | // Check to see if operand cycle already exists and create if it doesn't |
543 | uint16_t FindOperandCycle = 0; |
544 | if (NOperandCycles > 0) { |
545 | std::string ItinOperandString = |
546 | ItinOperandCycleString + ItinBypassString; |
547 | FindOperandCycle = ItinOperandMap[ItinOperandString]; |
548 | if (FindOperandCycle == 0) { |
549 | // Emit as cycle, // index |
550 | OperandCycleTable += ItinOperandCycleString + ", // " ; |
551 | std::string OperandIdxComment = itostr(X: OperandCycleCount); |
552 | if (NOperandCycles > 1) |
553 | OperandIdxComment += |
554 | "-" + itostr(X: OperandCycleCount + NOperandCycles - 1); |
555 | OperandCycleTable += OperandIdxComment + "\n" ; |
556 | // Record Itin class number. |
557 | ItinOperandMap[ItinOperandCycleString] = FindOperandCycle = |
558 | OperandCycleCount; |
559 | // Emit as bypass, // index |
560 | BypassTable += ItinBypassString + ", // " + OperandIdxComment + "\n" ; |
561 | OperandCycleCount += NOperandCycles; |
562 | } |
563 | } |
564 | |
565 | // Set up itinerary as location and location + stage count |
566 | int16_t NumUOps = ItinData ? ItinData->getValueAsInt(FieldName: "NumMicroOps" ) : 0; |
567 | InstrItinerary Intinerary = { |
568 | .NumMicroOps: NumUOps, |
569 | .FirstStage: FindStage, |
570 | .LastStage: uint16_t(FindStage + NStages), |
571 | .FirstOperandCycle: FindOperandCycle, |
572 | .LastOperandCycle: uint16_t(FindOperandCycle + NOperandCycles), |
573 | }; |
574 | |
575 | // Inject - empty slots will be 0, 0 |
576 | ItinList[SchedClassIdx] = Intinerary; |
577 | } |
578 | } |
579 | |
580 | // Closing stage |
581 | StageTable += " { 0, 0, 0, llvm::InstrStage::Required } // End stages\n" ; |
582 | StageTable += "};\n" ; |
583 | |
584 | // Closing operand cycles |
585 | OperandCycleTable += " 0 // End operand cycles\n" ; |
586 | OperandCycleTable += "};\n" ; |
587 | |
588 | BypassTable += " 0 // End bypass tables\n" ; |
589 | BypassTable += "};\n" ; |
590 | |
591 | // Emit tables. |
592 | OS << StageTable; |
593 | OS << OperandCycleTable; |
594 | OS << BypassTable; |
595 | } |
596 | |
597 | // |
598 | // EmitProcessorData - Generate data for processor itineraries that were |
599 | // computed during EmitStageAndOperandCycleData(). ProcItinLists lists all |
600 | // Itineraries for each processor. The Itinerary lists are indexed on |
601 | // CodeGenSchedClass::Index. |
602 | // |
603 | void SubtargetEmitter::EmitItineraries( |
604 | raw_ostream &OS, std::vector<std::vector<InstrItinerary>> &ProcItinLists) { |
605 | // Multiple processor models may share an itinerary record. Emit it once. |
606 | SmallPtrSet<Record *, 8> ItinsDefSet; |
607 | |
608 | // For each processor's machine model |
609 | std::vector<std::vector<InstrItinerary>>::iterator ProcItinListsIter = |
610 | ProcItinLists.begin(); |
611 | for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), |
612 | PE = SchedModels.procModelEnd(); |
613 | PI != PE; ++PI, ++ProcItinListsIter) { |
614 | |
615 | Record *ItinsDef = PI->ItinsDef; |
616 | if (!ItinsDefSet.insert(Ptr: ItinsDef).second) |
617 | continue; |
618 | |
619 | // Get the itinerary list for the processor. |
620 | assert(ProcItinListsIter != ProcItinLists.end() && "bad iterator" ); |
621 | std::vector<InstrItinerary> &ItinList = *ProcItinListsIter; |
622 | |
623 | // Empty itineraries aren't referenced anywhere in the tablegen output |
624 | // so don't emit them. |
625 | if (ItinList.empty()) |
626 | continue; |
627 | |
628 | OS << "\n" ; |
629 | OS << "static const llvm::InstrItinerary " ; |
630 | |
631 | // Begin processor itinerary table |
632 | OS << ItinsDef->getName() << "[] = {\n" ; |
633 | |
634 | // For each itinerary class in CodeGenSchedClass::Index order. |
635 | for (unsigned j = 0, M = ItinList.size(); j < M; ++j) { |
636 | InstrItinerary &Intinerary = ItinList[j]; |
637 | |
638 | // Emit Itinerary in the form of |
639 | // { firstStage, lastStage, firstCycle, lastCycle } // index |
640 | OS << " { " << Intinerary.NumMicroOps << ", " << Intinerary.FirstStage |
641 | << ", " << Intinerary.LastStage << ", " << Intinerary.FirstOperandCycle |
642 | << ", " << Intinerary.LastOperandCycle << " }" |
643 | << ", // " << j << " " << SchedModels.getSchedClass(Idx: j).Name << "\n" ; |
644 | } |
645 | // End processor itinerary table |
646 | OS << " { 0, uint16_t(~0U), uint16_t(~0U), uint16_t(~0U), uint16_t(~0U) }" |
647 | "// end marker\n" ; |
648 | OS << "};\n" ; |
649 | } |
650 | } |
651 | |
652 | // Emit either the value defined in the TableGen Record, or the default |
653 | // value defined in the C++ header. The Record is null if the processor does not |
654 | // define a model. |
655 | void SubtargetEmitter::EmitProcessorProp(raw_ostream &OS, const Record *R, |
656 | StringRef Name, char Separator) { |
657 | OS << " " ; |
658 | int V = R ? R->getValueAsInt(FieldName: Name) : -1; |
659 | if (V >= 0) |
660 | OS << V << Separator << " // " << Name; |
661 | else |
662 | OS << "MCSchedModel::Default" << Name << Separator; |
663 | OS << '\n'; |
664 | } |
665 | |
666 | void SubtargetEmitter::EmitProcessorResourceSubUnits( |
667 | const CodeGenProcModel &ProcModel, raw_ostream &OS) { |
668 | OS << "\nstatic const unsigned " << ProcModel.ModelName |
669 | << "ProcResourceSubUnits[] = {\n" |
670 | << " 0, // Invalid\n" ; |
671 | |
672 | for (unsigned i = 0, e = ProcModel.ProcResourceDefs.size(); i < e; ++i) { |
673 | Record *PRDef = ProcModel.ProcResourceDefs[i]; |
674 | if (!PRDef->isSubClassOf(Name: "ProcResGroup" )) |
675 | continue; |
676 | RecVec ResUnits = PRDef->getValueAsListOfDefs(FieldName: "Resources" ); |
677 | for (Record *RUDef : ResUnits) { |
678 | Record *const RU = |
679 | SchedModels.findProcResUnits(ProcResKind: RUDef, PM: ProcModel, Loc: PRDef->getLoc()); |
680 | for (unsigned J = 0; J < RU->getValueAsInt(FieldName: "NumUnits" ); ++J) { |
681 | OS << " " << ProcModel.getProcResourceIdx(PRDef: RU) << ", " ; |
682 | } |
683 | } |
684 | OS << " // " << PRDef->getName() << "\n" ; |
685 | } |
686 | OS << "};\n" ; |
687 | } |
688 | |
689 | static void EmitRetireControlUnitInfo(const CodeGenProcModel &ProcModel, |
690 | raw_ostream &OS) { |
691 | int64_t ReorderBufferSize = 0, MaxRetirePerCycle = 0; |
692 | if (Record *RCU = ProcModel.RetireControlUnit) { |
693 | ReorderBufferSize = |
694 | std::max(a: ReorderBufferSize, b: RCU->getValueAsInt(FieldName: "ReorderBufferSize" )); |
695 | MaxRetirePerCycle = |
696 | std::max(a: MaxRetirePerCycle, b: RCU->getValueAsInt(FieldName: "MaxRetirePerCycle" )); |
697 | } |
698 | |
699 | OS << ReorderBufferSize << ", // ReorderBufferSize\n " ; |
700 | OS << MaxRetirePerCycle << ", // MaxRetirePerCycle\n " ; |
701 | } |
702 | |
703 | static void EmitRegisterFileInfo(const CodeGenProcModel &ProcModel, |
704 | unsigned NumRegisterFiles, |
705 | unsigned NumCostEntries, raw_ostream &OS) { |
706 | if (NumRegisterFiles) |
707 | OS << ProcModel.ModelName << "RegisterFiles,\n " << (1 + NumRegisterFiles); |
708 | else |
709 | OS << "nullptr,\n 0" ; |
710 | |
711 | OS << ", // Number of register files.\n " ; |
712 | if (NumCostEntries) |
713 | OS << ProcModel.ModelName << "RegisterCosts,\n " ; |
714 | else |
715 | OS << "nullptr,\n " ; |
716 | OS << NumCostEntries << ", // Number of register cost entries.\n" ; |
717 | } |
718 | |
719 | unsigned |
720 | SubtargetEmitter::EmitRegisterFileTables(const CodeGenProcModel &ProcModel, |
721 | raw_ostream &OS) { |
722 | if (llvm::all_of(Range: ProcModel.RegisterFiles, P: [](const CodeGenRegisterFile &RF) { |
723 | return RF.hasDefaultCosts(); |
724 | })) |
725 | return 0; |
726 | |
727 | // Print the RegisterCost table first. |
728 | OS << "\n// {RegisterClassID, Register Cost, AllowMoveElimination }\n" ; |
729 | OS << "static const llvm::MCRegisterCostEntry " << ProcModel.ModelName |
730 | << "RegisterCosts" |
731 | << "[] = {\n" ; |
732 | |
733 | for (const CodeGenRegisterFile &RF : ProcModel.RegisterFiles) { |
734 | // Skip register files with a default cost table. |
735 | if (RF.hasDefaultCosts()) |
736 | continue; |
737 | // Add entries to the cost table. |
738 | for (const CodeGenRegisterCost &RC : RF.Costs) { |
739 | OS << " { " ; |
740 | Record *Rec = RC.RCDef; |
741 | if (Rec->getValue(Name: "Namespace" )) |
742 | OS << Rec->getValueAsString(FieldName: "Namespace" ) << "::" ; |
743 | OS << Rec->getName() << "RegClassID, " << RC.Cost << ", " |
744 | << RC.AllowMoveElimination << "},\n" ; |
745 | } |
746 | } |
747 | OS << "};\n" ; |
748 | |
749 | // Now generate a table with register file info. |
750 | OS << "\n // {Name, #PhysRegs, #CostEntries, IndexToCostTbl, " |
751 | << "MaxMovesEliminatedPerCycle, AllowZeroMoveEliminationOnly }\n" ; |
752 | OS << "static const llvm::MCRegisterFileDesc " << ProcModel.ModelName |
753 | << "RegisterFiles" |
754 | << "[] = {\n" |
755 | << " { \"InvalidRegisterFile\", 0, 0, 0, 0, 0 },\n" ; |
756 | unsigned CostTblIndex = 0; |
757 | |
758 | for (const CodeGenRegisterFile &RD : ProcModel.RegisterFiles) { |
759 | OS << " { " ; |
760 | OS << '"' << RD.Name << '"' << ", " << RD.NumPhysRegs << ", " ; |
761 | unsigned NumCostEntries = RD.Costs.size(); |
762 | OS << NumCostEntries << ", " << CostTblIndex << ", " |
763 | << RD.MaxMovesEliminatedPerCycle << ", " |
764 | << RD.AllowZeroMoveEliminationOnly << "},\n" ; |
765 | CostTblIndex += NumCostEntries; |
766 | } |
767 | OS << "};\n" ; |
768 | |
769 | return CostTblIndex; |
770 | } |
771 | |
772 | void SubtargetEmitter::EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel, |
773 | raw_ostream &OS) { |
774 | unsigned QueueID = 0; |
775 | if (ProcModel.LoadQueue) { |
776 | const Record *Queue = ProcModel.LoadQueue->getValueAsDef(FieldName: "QueueDescriptor" ); |
777 | QueueID = 1 + std::distance(first: ProcModel.ProcResourceDefs.begin(), |
778 | last: find(Range: ProcModel.ProcResourceDefs, Val: Queue)); |
779 | } |
780 | OS << " " << QueueID << ", // Resource Descriptor for the Load Queue\n" ; |
781 | |
782 | QueueID = 0; |
783 | if (ProcModel.StoreQueue) { |
784 | const Record *Queue = |
785 | ProcModel.StoreQueue->getValueAsDef(FieldName: "QueueDescriptor" ); |
786 | QueueID = 1 + std::distance(first: ProcModel.ProcResourceDefs.begin(), |
787 | last: find(Range: ProcModel.ProcResourceDefs, Val: Queue)); |
788 | } |
789 | OS << " " << QueueID << ", // Resource Descriptor for the Store Queue\n" ; |
790 | } |
791 | |
792 | void SubtargetEmitter::(const CodeGenProcModel &ProcModel, |
793 | raw_ostream &OS) { |
794 | // Generate a table of register file descriptors (one entry per each user |
795 | // defined register file), and a table of register costs. |
796 | unsigned NumCostEntries = EmitRegisterFileTables(ProcModel, OS); |
797 | |
798 | // Now generate a table for the extra processor info. |
799 | OS << "\nstatic const llvm::MCExtraProcessorInfo " << ProcModel.ModelName |
800 | << "ExtraInfo = {\n " ; |
801 | |
802 | // Add information related to the retire control unit. |
803 | EmitRetireControlUnitInfo(ProcModel, OS); |
804 | |
805 | // Add information related to the register files (i.e. where to find register |
806 | // file descriptors and register costs). |
807 | EmitRegisterFileInfo(ProcModel, NumRegisterFiles: ProcModel.RegisterFiles.size(), |
808 | NumCostEntries, OS); |
809 | |
810 | // Add information about load/store queues. |
811 | EmitLoadStoreQueueInfo(ProcModel, OS); |
812 | |
813 | OS << "};\n" ; |
814 | } |
815 | |
816 | void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, |
817 | raw_ostream &OS) { |
818 | EmitProcessorResourceSubUnits(ProcModel, OS); |
819 | |
820 | OS << "\n// {Name, NumUnits, SuperIdx, BufferSize, SubUnitsIdxBegin}\n" ; |
821 | OS << "static const llvm::MCProcResourceDesc " << ProcModel.ModelName |
822 | << "ProcResources" |
823 | << "[] = {\n" |
824 | << " {\"InvalidUnit\", 0, 0, 0, 0},\n" ; |
825 | |
826 | unsigned SubUnitsOffset = 1; |
827 | for (unsigned i = 0, e = ProcModel.ProcResourceDefs.size(); i < e; ++i) { |
828 | Record *PRDef = ProcModel.ProcResourceDefs[i]; |
829 | |
830 | Record *SuperDef = nullptr; |
831 | unsigned SuperIdx = 0; |
832 | unsigned NumUnits = 0; |
833 | const unsigned SubUnitsBeginOffset = SubUnitsOffset; |
834 | int BufferSize = PRDef->getValueAsInt(FieldName: "BufferSize" ); |
835 | if (PRDef->isSubClassOf(Name: "ProcResGroup" )) { |
836 | RecVec ResUnits = PRDef->getValueAsListOfDefs(FieldName: "Resources" ); |
837 | for (Record *RU : ResUnits) { |
838 | NumUnits += RU->getValueAsInt(FieldName: "NumUnits" ); |
839 | SubUnitsOffset += RU->getValueAsInt(FieldName: "NumUnits" ); |
840 | } |
841 | } else { |
842 | // Find the SuperIdx |
843 | if (PRDef->getValueInit(FieldName: "Super" )->isComplete()) { |
844 | SuperDef = SchedModels.findProcResUnits(ProcResKind: PRDef->getValueAsDef(FieldName: "Super" ), |
845 | PM: ProcModel, Loc: PRDef->getLoc()); |
846 | SuperIdx = ProcModel.getProcResourceIdx(PRDef: SuperDef); |
847 | } |
848 | NumUnits = PRDef->getValueAsInt(FieldName: "NumUnits" ); |
849 | } |
850 | // Emit the ProcResourceDesc |
851 | OS << " {\"" << PRDef->getName() << "\", " ; |
852 | if (PRDef->getName().size() < 15) |
853 | OS.indent(NumSpaces: 15 - PRDef->getName().size()); |
854 | OS << NumUnits << ", " << SuperIdx << ", " << BufferSize << ", " ; |
855 | if (SubUnitsBeginOffset != SubUnitsOffset) { |
856 | OS << ProcModel.ModelName << "ProcResourceSubUnits + " |
857 | << SubUnitsBeginOffset; |
858 | } else { |
859 | OS << "nullptr" ; |
860 | } |
861 | OS << "}, // #" << i + 1; |
862 | if (SuperDef) |
863 | OS << ", Super=" << SuperDef->getName(); |
864 | OS << "\n" ; |
865 | } |
866 | OS << "};\n" ; |
867 | } |
868 | |
869 | // Find the WriteRes Record that defines processor resources for this |
870 | // SchedWrite. |
871 | Record * |
872 | SubtargetEmitter::FindWriteResources(const CodeGenSchedRW &SchedWrite, |
873 | const CodeGenProcModel &ProcModel) { |
874 | |
875 | // Check if the SchedWrite is already subtarget-specific and directly |
876 | // specifies a set of processor resources. |
877 | if (SchedWrite.TheDef->isSubClassOf(Name: "SchedWriteRes" )) |
878 | return SchedWrite.TheDef; |
879 | |
880 | Record *AliasDef = nullptr; |
881 | for (Record *A : SchedWrite.Aliases) { |
882 | const CodeGenSchedRW &AliasRW = |
883 | SchedModels.getSchedRW(Def: A->getValueAsDef(FieldName: "AliasRW" )); |
884 | if (AliasRW.TheDef->getValueInit(FieldName: "SchedModel" )->isComplete()) { |
885 | Record *ModelDef = AliasRW.TheDef->getValueAsDef(FieldName: "SchedModel" ); |
886 | if (&SchedModels.getProcModel(ModelDef) != &ProcModel) |
887 | continue; |
888 | } |
889 | if (AliasDef) |
890 | PrintFatalError(ErrorLoc: AliasRW.TheDef->getLoc(), |
891 | Msg: "Multiple aliases " |
892 | "defined for processor " + |
893 | ProcModel.ModelName + |
894 | " Ensure only one SchedAlias exists per RW." ); |
895 | AliasDef = AliasRW.TheDef; |
896 | } |
897 | if (AliasDef && AliasDef->isSubClassOf(Name: "SchedWriteRes" )) |
898 | return AliasDef; |
899 | |
900 | // Check this processor's list of write resources. |
901 | Record *ResDef = nullptr; |
902 | for (Record *WR : ProcModel.WriteResDefs) { |
903 | if (!WR->isSubClassOf(Name: "WriteRes" )) |
904 | continue; |
905 | if (AliasDef == WR->getValueAsDef(FieldName: "WriteType" ) || |
906 | SchedWrite.TheDef == WR->getValueAsDef(FieldName: "WriteType" )) { |
907 | if (ResDef) { |
908 | PrintFatalError(ErrorLoc: WR->getLoc(), Msg: "Resources are defined for both " |
909 | "SchedWrite and its alias on processor " + |
910 | ProcModel.ModelName); |
911 | } |
912 | ResDef = WR; |
913 | } |
914 | } |
915 | // TODO: If ProcModel has a base model (previous generation processor), |
916 | // then call FindWriteResources recursively with that model here. |
917 | if (!ResDef) { |
918 | PrintFatalError(ErrorLoc: ProcModel.ModelDef->getLoc(), |
919 | Msg: Twine("Processor does not define resources for " ) + |
920 | SchedWrite.TheDef->getName()); |
921 | } |
922 | return ResDef; |
923 | } |
924 | |
925 | /// Find the ReadAdvance record for the given SchedRead on this processor or |
926 | /// return NULL. |
927 | Record *SubtargetEmitter::FindReadAdvance(const CodeGenSchedRW &SchedRead, |
928 | const CodeGenProcModel &ProcModel) { |
929 | // Check for SchedReads that directly specify a ReadAdvance. |
930 | if (SchedRead.TheDef->isSubClassOf(Name: "SchedReadAdvance" )) |
931 | return SchedRead.TheDef; |
932 | |
933 | // Check this processor's list of aliases for SchedRead. |
934 | Record *AliasDef = nullptr; |
935 | for (Record *A : SchedRead.Aliases) { |
936 | const CodeGenSchedRW &AliasRW = |
937 | SchedModels.getSchedRW(Def: A->getValueAsDef(FieldName: "AliasRW" )); |
938 | if (AliasRW.TheDef->getValueInit(FieldName: "SchedModel" )->isComplete()) { |
939 | Record *ModelDef = AliasRW.TheDef->getValueAsDef(FieldName: "SchedModel" ); |
940 | if (&SchedModels.getProcModel(ModelDef) != &ProcModel) |
941 | continue; |
942 | } |
943 | if (AliasDef) |
944 | PrintFatalError(ErrorLoc: AliasRW.TheDef->getLoc(), |
945 | Msg: "Multiple aliases " |
946 | "defined for processor " + |
947 | ProcModel.ModelName + |
948 | " Ensure only one SchedAlias exists per RW." ); |
949 | AliasDef = AliasRW.TheDef; |
950 | } |
951 | if (AliasDef && AliasDef->isSubClassOf(Name: "SchedReadAdvance" )) |
952 | return AliasDef; |
953 | |
954 | // Check this processor's ReadAdvanceList. |
955 | Record *ResDef = nullptr; |
956 | for (Record *RA : ProcModel.ReadAdvanceDefs) { |
957 | if (!RA->isSubClassOf(Name: "ReadAdvance" )) |
958 | continue; |
959 | if (AliasDef == RA->getValueAsDef(FieldName: "ReadType" ) || |
960 | SchedRead.TheDef == RA->getValueAsDef(FieldName: "ReadType" )) { |
961 | if (ResDef) { |
962 | PrintFatalError(ErrorLoc: RA->getLoc(), Msg: "Resources are defined for both " |
963 | "SchedRead and its alias on processor " + |
964 | ProcModel.ModelName); |
965 | } |
966 | ResDef = RA; |
967 | } |
968 | } |
969 | // TODO: If ProcModel has a base model (previous generation processor), |
970 | // then call FindReadAdvance recursively with that model here. |
971 | if (!ResDef && SchedRead.TheDef->getName() != "ReadDefault" ) { |
972 | PrintFatalError(ErrorLoc: ProcModel.ModelDef->getLoc(), |
973 | Msg: Twine("Processor does not define resources for " ) + |
974 | SchedRead.TheDef->getName()); |
975 | } |
976 | return ResDef; |
977 | } |
978 | |
979 | // Expand an explicit list of processor resources into a full list of implied |
980 | // resource groups and super resources that cover them. |
981 | void SubtargetEmitter::ExpandProcResources( |
982 | RecVec &PRVec, std::vector<int64_t> &ReleaseAtCycles, |
983 | std::vector<int64_t> &AcquireAtCycles, const CodeGenProcModel &PM) { |
984 | assert(PRVec.size() == ReleaseAtCycles.size() && "failed precondition" ); |
985 | for (unsigned i = 0, e = PRVec.size(); i != e; ++i) { |
986 | Record *PRDef = PRVec[i]; |
987 | RecVec SubResources; |
988 | if (PRDef->isSubClassOf(Name: "ProcResGroup" )) |
989 | SubResources = PRDef->getValueAsListOfDefs(FieldName: "Resources" ); |
990 | else { |
991 | SubResources.push_back(x: PRDef); |
992 | PRDef = SchedModels.findProcResUnits(ProcResKind: PRDef, PM, Loc: PRDef->getLoc()); |
993 | for (Record *SubDef = PRDef; |
994 | SubDef->getValueInit(FieldName: "Super" )->isComplete();) { |
995 | if (SubDef->isSubClassOf(Name: "ProcResGroup" )) { |
996 | // Disallow this for simplicitly. |
997 | PrintFatalError(ErrorLoc: SubDef->getLoc(), Msg: "Processor resource group " |
998 | " cannot be a super resources." ); |
999 | } |
1000 | Record *SuperDef = SchedModels.findProcResUnits( |
1001 | ProcResKind: SubDef->getValueAsDef(FieldName: "Super" ), PM, Loc: SubDef->getLoc()); |
1002 | PRVec.push_back(x: SuperDef); |
1003 | ReleaseAtCycles.push_back(x: ReleaseAtCycles[i]); |
1004 | AcquireAtCycles.push_back(x: AcquireAtCycles[i]); |
1005 | SubDef = SuperDef; |
1006 | } |
1007 | } |
1008 | for (Record *PR : PM.ProcResourceDefs) { |
1009 | if (PR == PRDef || !PR->isSubClassOf(Name: "ProcResGroup" )) |
1010 | continue; |
1011 | RecVec SuperResources = PR->getValueAsListOfDefs(FieldName: "Resources" ); |
1012 | RecIter SubI = SubResources.begin(), SubE = SubResources.end(); |
1013 | for (; SubI != SubE; ++SubI) { |
1014 | if (!is_contained(Range&: SuperResources, Element: *SubI)) { |
1015 | break; |
1016 | } |
1017 | } |
1018 | if (SubI == SubE) { |
1019 | PRVec.push_back(x: PR); |
1020 | ReleaseAtCycles.push_back(x: ReleaseAtCycles[i]); |
1021 | AcquireAtCycles.push_back(x: AcquireAtCycles[i]); |
1022 | } |
1023 | } |
1024 | } |
1025 | } |
1026 | |
1027 | // Generate the SchedClass table for this processor and update global |
1028 | // tables. Must be called for each processor in order. |
1029 | void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, |
1030 | SchedClassTables &SchedTables) { |
1031 | std::vector<MCSchedClassDesc> &SCTab = |
1032 | SchedTables.ProcSchedClasses.emplace_back(); |
1033 | if (!ProcModel.hasInstrSchedModel()) |
1034 | return; |
1035 | |
1036 | LLVM_DEBUG(dbgs() << "\n+++ SCHED CLASSES (GenSchedClassTables) +++\n" ); |
1037 | for (const CodeGenSchedClass &SC : SchedModels.schedClasses()) { |
1038 | LLVM_DEBUG(SC.dump(&SchedModels)); |
1039 | |
1040 | MCSchedClassDesc &SCDesc = SCTab.emplace_back(); |
1041 | // SCDesc.Name is guarded by NDEBUG |
1042 | SCDesc.NumMicroOps = 0; |
1043 | SCDesc.BeginGroup = false; |
1044 | SCDesc.EndGroup = false; |
1045 | SCDesc.RetireOOO = false; |
1046 | SCDesc.WriteProcResIdx = 0; |
1047 | SCDesc.WriteLatencyIdx = 0; |
1048 | SCDesc.ReadAdvanceIdx = 0; |
1049 | |
1050 | // A Variant SchedClass has no resources of its own. |
1051 | bool HasVariants = false; |
1052 | for (const CodeGenSchedTransition &CGT : |
1053 | make_range(x: SC.Transitions.begin(), y: SC.Transitions.end())) { |
1054 | if (CGT.ProcIndex == ProcModel.Index) { |
1055 | HasVariants = true; |
1056 | break; |
1057 | } |
1058 | } |
1059 | if (HasVariants) { |
1060 | SCDesc.NumMicroOps = MCSchedClassDesc::VariantNumMicroOps; |
1061 | continue; |
1062 | } |
1063 | |
1064 | // Determine if the SchedClass is actually reachable on this processor. If |
1065 | // not don't try to locate the processor resources, it will fail. |
1066 | // If ProcIndices contains 0, this class applies to all processors. |
1067 | assert(!SC.ProcIndices.empty() && "expect at least one procidx" ); |
1068 | if (SC.ProcIndices[0] != 0) { |
1069 | if (!is_contained(Range: SC.ProcIndices, Element: ProcModel.Index)) |
1070 | continue; |
1071 | } |
1072 | IdxVec Writes = SC.Writes; |
1073 | IdxVec Reads = SC.Reads; |
1074 | if (!SC.InstRWs.empty()) { |
1075 | // This class has a default ReadWrite list which can be overridden by |
1076 | // InstRW definitions. |
1077 | Record *RWDef = nullptr; |
1078 | for (Record *RW : SC.InstRWs) { |
1079 | Record *RWModelDef = RW->getValueAsDef(FieldName: "SchedModel" ); |
1080 | if (&ProcModel == &SchedModels.getProcModel(ModelDef: RWModelDef)) { |
1081 | RWDef = RW; |
1082 | break; |
1083 | } |
1084 | } |
1085 | if (RWDef) { |
1086 | Writes.clear(); |
1087 | Reads.clear(); |
1088 | SchedModels.findRWs(RWDefs: RWDef->getValueAsListOfDefs(FieldName: "OperandReadWrites" ), |
1089 | Writes, Reads); |
1090 | } |
1091 | } |
1092 | if (Writes.empty()) { |
1093 | // Check this processor's itinerary class resources. |
1094 | for (Record *I : ProcModel.ItinRWDefs) { |
1095 | RecVec Matched = I->getValueAsListOfDefs(FieldName: "MatchedItinClasses" ); |
1096 | if (is_contained(Range&: Matched, Element: SC.ItinClassDef)) { |
1097 | SchedModels.findRWs(RWDefs: I->getValueAsListOfDefs(FieldName: "OperandReadWrites" ), |
1098 | Writes, Reads); |
1099 | break; |
1100 | } |
1101 | } |
1102 | if (Writes.empty()) { |
1103 | LLVM_DEBUG(dbgs() << ProcModel.ModelName |
1104 | << " does not have resources for class " << SC.Name |
1105 | << '\n'); |
1106 | SCDesc.NumMicroOps = MCSchedClassDesc::InvalidNumMicroOps; |
1107 | } |
1108 | } |
1109 | // Sum resources across all operand writes. |
1110 | std::vector<MCWriteProcResEntry> WriteProcResources; |
1111 | std::vector<MCWriteLatencyEntry> WriteLatencies; |
1112 | std::vector<std::string> WriterNames; |
1113 | std::vector<MCReadAdvanceEntry> ReadAdvanceEntries; |
1114 | for (unsigned W : Writes) { |
1115 | IdxVec WriteSeq; |
1116 | SchedModels.expandRWSeqForProc(RWIdx: W, RWSeq&: WriteSeq, /*IsRead=*/false, ProcModel); |
1117 | |
1118 | // For each operand, create a latency entry. |
1119 | MCWriteLatencyEntry WLEntry; |
1120 | WLEntry.Cycles = 0; |
1121 | unsigned WriteID = WriteSeq.back(); |
1122 | WriterNames.push_back(x: SchedModels.getSchedWrite(Idx: WriteID).Name); |
1123 | // If this Write is not referenced by a ReadAdvance, don't distinguish it |
1124 | // from other WriteLatency entries. |
1125 | if (!SchedModels.hasReadOfWrite( |
1126 | WriteDef: SchedModels.getSchedWrite(Idx: WriteID).TheDef)) { |
1127 | WriteID = 0; |
1128 | } |
1129 | WLEntry.WriteResourceID = WriteID; |
1130 | |
1131 | for (unsigned WS : WriteSeq) { |
1132 | |
1133 | Record *WriteRes = |
1134 | FindWriteResources(SchedWrite: SchedModels.getSchedWrite(Idx: WS), ProcModel); |
1135 | |
1136 | // Mark the parent class as invalid for unsupported write types. |
1137 | if (WriteRes->getValueAsBit(FieldName: "Unsupported" )) { |
1138 | SCDesc.NumMicroOps = MCSchedClassDesc::InvalidNumMicroOps; |
1139 | break; |
1140 | } |
1141 | WLEntry.Cycles += WriteRes->getValueAsInt(FieldName: "Latency" ); |
1142 | SCDesc.NumMicroOps += WriteRes->getValueAsInt(FieldName: "NumMicroOps" ); |
1143 | SCDesc.BeginGroup |= WriteRes->getValueAsBit(FieldName: "BeginGroup" ); |
1144 | SCDesc.EndGroup |= WriteRes->getValueAsBit(FieldName: "EndGroup" ); |
1145 | SCDesc.BeginGroup |= WriteRes->getValueAsBit(FieldName: "SingleIssue" ); |
1146 | SCDesc.EndGroup |= WriteRes->getValueAsBit(FieldName: "SingleIssue" ); |
1147 | SCDesc.RetireOOO |= WriteRes->getValueAsBit(FieldName: "RetireOOO" ); |
1148 | |
1149 | // Create an entry for each ProcResource listed in WriteRes. |
1150 | RecVec PRVec = WriteRes->getValueAsListOfDefs(FieldName: "ProcResources" ); |
1151 | std::vector<int64_t> ReleaseAtCycles = |
1152 | WriteRes->getValueAsListOfInts(FieldName: "ReleaseAtCycles" ); |
1153 | |
1154 | std::vector<int64_t> AcquireAtCycles = |
1155 | WriteRes->getValueAsListOfInts(FieldName: "AcquireAtCycles" ); |
1156 | |
1157 | // Check consistency of the two vectors carrying the start and |
1158 | // stop cycles of the resources. |
1159 | if (!ReleaseAtCycles.empty() && |
1160 | ReleaseAtCycles.size() != PRVec.size()) { |
1161 | // If ReleaseAtCycles is provided, check consistency. |
1162 | PrintFatalError( |
1163 | ErrorLoc: WriteRes->getLoc(), |
1164 | Msg: Twine("Inconsistent release at cycles: size(ReleaseAtCycles) != " |
1165 | "size(ProcResources): " ) |
1166 | .concat(Suffix: Twine(PRVec.size())) |
1167 | .concat(Suffix: " vs " ) |
1168 | .concat(Suffix: Twine(ReleaseAtCycles.size()))); |
1169 | } |
1170 | |
1171 | if (!AcquireAtCycles.empty() && |
1172 | AcquireAtCycles.size() != PRVec.size()) { |
1173 | PrintFatalError( |
1174 | ErrorLoc: WriteRes->getLoc(), |
1175 | Msg: Twine("Inconsistent resource cycles: size(AcquireAtCycles) != " |
1176 | "size(ProcResources): " ) |
1177 | .concat(Suffix: Twine(AcquireAtCycles.size())) |
1178 | .concat(Suffix: " vs " ) |
1179 | .concat(Suffix: Twine(PRVec.size()))); |
1180 | } |
1181 | |
1182 | if (ReleaseAtCycles.empty()) { |
1183 | // If ReleaseAtCycles is not provided, default to one cycle |
1184 | // per resource. |
1185 | ReleaseAtCycles.resize(new_size: PRVec.size(), x: 1); |
1186 | } |
1187 | |
1188 | if (AcquireAtCycles.empty()) { |
1189 | // If AcquireAtCycles is not provided, reserve the resource |
1190 | // starting from cycle 0. |
1191 | AcquireAtCycles.resize(new_size: PRVec.size(), x: 0); |
1192 | } |
1193 | |
1194 | assert(AcquireAtCycles.size() == ReleaseAtCycles.size()); |
1195 | |
1196 | ExpandProcResources(PRVec, ReleaseAtCycles, AcquireAtCycles, PM: ProcModel); |
1197 | assert(AcquireAtCycles.size() == ReleaseAtCycles.size()); |
1198 | |
1199 | for (unsigned PRIdx = 0, PREnd = PRVec.size(); PRIdx != PREnd; |
1200 | ++PRIdx) { |
1201 | MCWriteProcResEntry WPREntry; |
1202 | WPREntry.ProcResourceIdx = ProcModel.getProcResourceIdx(PRDef: PRVec[PRIdx]); |
1203 | assert(WPREntry.ProcResourceIdx && "Bad ProcResourceIdx" ); |
1204 | WPREntry.ReleaseAtCycle = ReleaseAtCycles[PRIdx]; |
1205 | WPREntry.AcquireAtCycle = AcquireAtCycles[PRIdx]; |
1206 | if (AcquireAtCycles[PRIdx] > ReleaseAtCycles[PRIdx]) { |
1207 | PrintFatalError( |
1208 | ErrorLoc: WriteRes->getLoc(), |
1209 | Msg: Twine("Inconsistent resource cycles: AcquireAtCycles " |
1210 | "< ReleaseAtCycles must hold." )); |
1211 | } |
1212 | if (AcquireAtCycles[PRIdx] < 0) { |
1213 | PrintFatalError(ErrorLoc: WriteRes->getLoc(), |
1214 | Msg: Twine("Invalid value: AcquireAtCycle " |
1215 | "must be a non-negative value." )); |
1216 | } |
1217 | // If this resource is already used in this sequence, add the current |
1218 | // entry's cycles so that the same resource appears to be used |
1219 | // serially, rather than multiple parallel uses. This is important for |
1220 | // in-order machine where the resource consumption is a hazard. |
1221 | unsigned WPRIdx = 0, WPREnd = WriteProcResources.size(); |
1222 | for (; WPRIdx != WPREnd; ++WPRIdx) { |
1223 | if (WriteProcResources[WPRIdx].ProcResourceIdx == |
1224 | WPREntry.ProcResourceIdx) { |
1225 | // TODO: multiple use of the same resources would |
1226 | // require either 1. thinking of how to handle multiple |
1227 | // intervals for the same resource in |
1228 | // `<Target>WriteProcResTable` (see |
1229 | // `SubtargetEmitter::EmitSchedClassTables`), or |
1230 | // 2. thinking how to merge multiple intervals into a |
1231 | // single interval. |
1232 | assert(WPREntry.AcquireAtCycle == 0 && |
1233 | "multiple use ofthe same resource is not yet handled" ); |
1234 | WriteProcResources[WPRIdx].ReleaseAtCycle += |
1235 | WPREntry.ReleaseAtCycle; |
1236 | break; |
1237 | } |
1238 | } |
1239 | if (WPRIdx == WPREnd) |
1240 | WriteProcResources.push_back(x: WPREntry); |
1241 | } |
1242 | } |
1243 | WriteLatencies.push_back(x: WLEntry); |
1244 | } |
1245 | // Create an entry for each operand Read in this SchedClass. |
1246 | // Entries must be sorted first by UseIdx then by WriteResourceID. |
1247 | for (unsigned UseIdx = 0, EndIdx = Reads.size(); UseIdx != EndIdx; |
1248 | ++UseIdx) { |
1249 | Record *ReadAdvance = |
1250 | FindReadAdvance(SchedRead: SchedModels.getSchedRead(Idx: Reads[UseIdx]), ProcModel); |
1251 | if (!ReadAdvance) |
1252 | continue; |
1253 | |
1254 | // Mark the parent class as invalid for unsupported write types. |
1255 | if (ReadAdvance->getValueAsBit(FieldName: "Unsupported" )) { |
1256 | SCDesc.NumMicroOps = MCSchedClassDesc::InvalidNumMicroOps; |
1257 | break; |
1258 | } |
1259 | RecVec ValidWrites = ReadAdvance->getValueAsListOfDefs(FieldName: "ValidWrites" ); |
1260 | IdxVec WriteIDs; |
1261 | if (ValidWrites.empty()) |
1262 | WriteIDs.push_back(x: 0); |
1263 | else { |
1264 | for (Record *VW : ValidWrites) { |
1265 | unsigned WriteID = SchedModels.getSchedRWIdx(Def: VW, /*IsRead=*/false); |
1266 | assert(WriteID != 0 && |
1267 | "Expected a valid SchedRW in the list of ValidWrites" ); |
1268 | WriteIDs.push_back(x: WriteID); |
1269 | } |
1270 | } |
1271 | llvm::sort(C&: WriteIDs); |
1272 | for (unsigned W : WriteIDs) { |
1273 | MCReadAdvanceEntry RAEntry; |
1274 | RAEntry.UseIdx = UseIdx; |
1275 | RAEntry.WriteResourceID = W; |
1276 | RAEntry.Cycles = ReadAdvance->getValueAsInt(FieldName: "Cycles" ); |
1277 | ReadAdvanceEntries.push_back(x: RAEntry); |
1278 | } |
1279 | } |
1280 | if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) { |
1281 | WriteProcResources.clear(); |
1282 | WriteLatencies.clear(); |
1283 | ReadAdvanceEntries.clear(); |
1284 | } |
1285 | // Add the information for this SchedClass to the global tables using basic |
1286 | // compression. |
1287 | // |
1288 | // WritePrecRes entries are sorted by ProcResIdx. |
1289 | llvm::sort(C&: WriteProcResources, Comp: LessWriteProcResources()); |
1290 | |
1291 | SCDesc.NumWriteProcResEntries = WriteProcResources.size(); |
1292 | std::vector<MCWriteProcResEntry>::iterator WPRPos = |
1293 | std::search(first1: SchedTables.WriteProcResources.begin(), |
1294 | last1: SchedTables.WriteProcResources.end(), |
1295 | first2: WriteProcResources.begin(), last2: WriteProcResources.end()); |
1296 | if (WPRPos != SchedTables.WriteProcResources.end()) |
1297 | SCDesc.WriteProcResIdx = WPRPos - SchedTables.WriteProcResources.begin(); |
1298 | else { |
1299 | SCDesc.WriteProcResIdx = SchedTables.WriteProcResources.size(); |
1300 | SchedTables.WriteProcResources.insert(position: WPRPos, first: WriteProcResources.begin(), |
1301 | last: WriteProcResources.end()); |
1302 | } |
1303 | // Latency entries must remain in operand order. |
1304 | SCDesc.NumWriteLatencyEntries = WriteLatencies.size(); |
1305 | std::vector<MCWriteLatencyEntry>::iterator WLPos = std::search( |
1306 | first1: SchedTables.WriteLatencies.begin(), last1: SchedTables.WriteLatencies.end(), |
1307 | first2: WriteLatencies.begin(), last2: WriteLatencies.end()); |
1308 | if (WLPos != SchedTables.WriteLatencies.end()) { |
1309 | unsigned idx = WLPos - SchedTables.WriteLatencies.begin(); |
1310 | SCDesc.WriteLatencyIdx = idx; |
1311 | for (unsigned i = 0, e = WriteLatencies.size(); i < e; ++i) |
1312 | if (SchedTables.WriterNames[idx + i].find(str: WriterNames[i]) == |
1313 | std::string::npos) { |
1314 | SchedTables.WriterNames[idx + i] += std::string("_" ) + WriterNames[i]; |
1315 | } |
1316 | } else { |
1317 | SCDesc.WriteLatencyIdx = SchedTables.WriteLatencies.size(); |
1318 | llvm::append_range(C&: SchedTables.WriteLatencies, R&: WriteLatencies); |
1319 | llvm::append_range(C&: SchedTables.WriterNames, R&: WriterNames); |
1320 | } |
1321 | // ReadAdvanceEntries must remain in operand order. |
1322 | SCDesc.NumReadAdvanceEntries = ReadAdvanceEntries.size(); |
1323 | std::vector<MCReadAdvanceEntry>::iterator RAPos = |
1324 | std::search(first1: SchedTables.ReadAdvanceEntries.begin(), |
1325 | last1: SchedTables.ReadAdvanceEntries.end(), |
1326 | first2: ReadAdvanceEntries.begin(), last2: ReadAdvanceEntries.end()); |
1327 | if (RAPos != SchedTables.ReadAdvanceEntries.end()) |
1328 | SCDesc.ReadAdvanceIdx = RAPos - SchedTables.ReadAdvanceEntries.begin(); |
1329 | else { |
1330 | SCDesc.ReadAdvanceIdx = SchedTables.ReadAdvanceEntries.size(); |
1331 | llvm::append_range(C&: SchedTables.ReadAdvanceEntries, R&: ReadAdvanceEntries); |
1332 | } |
1333 | } |
1334 | } |
1335 | |
1336 | // Emit SchedClass tables for all processors and associated global tables. |
1337 | void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, |
1338 | raw_ostream &OS) { |
1339 | // Emit global WriteProcResTable. |
1340 | OS << "\n// {ProcResourceIdx, ReleaseAtCycle, AcquireAtCycle}\n" |
1341 | << "extern const llvm::MCWriteProcResEntry " << Target |
1342 | << "WriteProcResTable[] = {\n" |
1343 | << " { 0, 0, 0 }, // Invalid\n" ; |
1344 | for (unsigned WPRIdx = 1, WPREnd = SchedTables.WriteProcResources.size(); |
1345 | WPRIdx != WPREnd; ++WPRIdx) { |
1346 | MCWriteProcResEntry &WPREntry = SchedTables.WriteProcResources[WPRIdx]; |
1347 | OS << " {" << format(Fmt: "%2d" , Vals: WPREntry.ProcResourceIdx) << ", " |
1348 | << format(Fmt: "%2d" , Vals: WPREntry.ReleaseAtCycle) << ", " |
1349 | << format(Fmt: "%2d" , Vals: WPREntry.AcquireAtCycle) << "}" ; |
1350 | if (WPRIdx + 1 < WPREnd) |
1351 | OS << ','; |
1352 | OS << " // #" << WPRIdx << '\n'; |
1353 | } |
1354 | OS << "}; // " << Target << "WriteProcResTable\n" ; |
1355 | |
1356 | // Emit global WriteLatencyTable. |
1357 | OS << "\n// {Cycles, WriteResourceID}\n" |
1358 | << "extern const llvm::MCWriteLatencyEntry " << Target |
1359 | << "WriteLatencyTable[] = {\n" |
1360 | << " { 0, 0}, // Invalid\n" ; |
1361 | for (unsigned WLIdx = 1, WLEnd = SchedTables.WriteLatencies.size(); |
1362 | WLIdx != WLEnd; ++WLIdx) { |
1363 | MCWriteLatencyEntry &WLEntry = SchedTables.WriteLatencies[WLIdx]; |
1364 | OS << " {" << format(Fmt: "%2d" , Vals: WLEntry.Cycles) << ", " |
1365 | << format(Fmt: "%2d" , Vals: WLEntry.WriteResourceID) << "}" ; |
1366 | if (WLIdx + 1 < WLEnd) |
1367 | OS << ','; |
1368 | OS << " // #" << WLIdx << " " << SchedTables.WriterNames[WLIdx] << '\n'; |
1369 | } |
1370 | OS << "}; // " << Target << "WriteLatencyTable\n" ; |
1371 | |
1372 | // Emit global ReadAdvanceTable. |
1373 | OS << "\n// {UseIdx, WriteResourceID, Cycles}\n" |
1374 | << "extern const llvm::MCReadAdvanceEntry " << Target |
1375 | << "ReadAdvanceTable[] = {\n" |
1376 | << " {0, 0, 0}, // Invalid\n" ; |
1377 | for (unsigned RAIdx = 1, RAEnd = SchedTables.ReadAdvanceEntries.size(); |
1378 | RAIdx != RAEnd; ++RAIdx) { |
1379 | MCReadAdvanceEntry &RAEntry = SchedTables.ReadAdvanceEntries[RAIdx]; |
1380 | OS << " {" << RAEntry.UseIdx << ", " |
1381 | << format(Fmt: "%2d" , Vals: RAEntry.WriteResourceID) << ", " |
1382 | << format(Fmt: "%2d" , Vals: RAEntry.Cycles) << "}" ; |
1383 | if (RAIdx + 1 < RAEnd) |
1384 | OS << ','; |
1385 | OS << " // #" << RAIdx << '\n'; |
1386 | } |
1387 | OS << "}; // " << Target << "ReadAdvanceTable\n" ; |
1388 | |
1389 | // Emit a SchedClass table for each processor. |
1390 | for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), |
1391 | PE = SchedModels.procModelEnd(); |
1392 | PI != PE; ++PI) { |
1393 | if (!PI->hasInstrSchedModel()) |
1394 | continue; |
1395 | |
1396 | std::vector<MCSchedClassDesc> &SCTab = |
1397 | SchedTables.ProcSchedClasses[1 + (PI - SchedModels.procModelBegin())]; |
1398 | |
1399 | OS << "\n// {Name, NumMicroOps, BeginGroup, EndGroup, RetireOOO," |
1400 | << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n" ; |
1401 | OS << "static const llvm::MCSchedClassDesc " << PI->ModelName |
1402 | << "SchedClasses[] = {\n" ; |
1403 | |
1404 | // The first class is always invalid. We no way to distinguish it except by |
1405 | // name and position. |
1406 | assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" && |
1407 | "invalid class not first" ); |
1408 | OS << " {DBGFIELD(\"InvalidSchedClass\") " |
1409 | << MCSchedClassDesc::InvalidNumMicroOps |
1410 | << ", false, false, false, 0, 0, 0, 0, 0, 0},\n" ; |
1411 | |
1412 | for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) { |
1413 | MCSchedClassDesc &MCDesc = SCTab[SCIdx]; |
1414 | const CodeGenSchedClass &SchedClass = SchedModels.getSchedClass(Idx: SCIdx); |
1415 | OS << " {DBGFIELD(\"" << SchedClass.Name << "\") " ; |
1416 | if (SchedClass.Name.size() < 18) |
1417 | OS.indent(NumSpaces: 18 - SchedClass.Name.size()); |
1418 | OS << MCDesc.NumMicroOps << ", " << (MCDesc.BeginGroup ? "true" : "false" ) |
1419 | << ", " << (MCDesc.EndGroup ? "true" : "false" ) << ", " |
1420 | << (MCDesc.RetireOOO ? "true" : "false" ) << ", " |
1421 | << format(Fmt: "%2d" , Vals: MCDesc.WriteProcResIdx) << ", " |
1422 | << MCDesc.NumWriteProcResEntries << ", " |
1423 | << format(Fmt: "%2d" , Vals: MCDesc.WriteLatencyIdx) << ", " |
1424 | << MCDesc.NumWriteLatencyEntries << ", " |
1425 | << format(Fmt: "%2d" , Vals: MCDesc.ReadAdvanceIdx) << ", " |
1426 | << MCDesc.NumReadAdvanceEntries << "}, // #" << SCIdx << '\n'; |
1427 | } |
1428 | OS << "}; // " << PI->ModelName << "SchedClasses\n" ; |
1429 | } |
1430 | } |
1431 | |
1432 | void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { |
1433 | // For each processor model. |
1434 | for (const CodeGenProcModel &PM : SchedModels.procModels()) { |
1435 | // Emit extra processor info if available. |
1436 | if (PM.hasExtraProcessorInfo()) |
1437 | EmitExtraProcessorInfo(ProcModel: PM, OS); |
1438 | // Emit processor resource table. |
1439 | if (PM.hasInstrSchedModel()) |
1440 | EmitProcessorResources(ProcModel: PM, OS); |
1441 | else if (!PM.ProcResourceDefs.empty()) |
1442 | PrintFatalError(ErrorLoc: PM.ModelDef->getLoc(), |
1443 | Msg: "SchedMachineModel defines " |
1444 | "ProcResources without defining WriteRes SchedWriteRes" ); |
1445 | |
1446 | // Begin processor itinerary properties |
1447 | OS << "\n" ; |
1448 | OS << "static const llvm::MCSchedModel " << PM.ModelName << " = {\n" ; |
1449 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "IssueWidth" , Separator: ','); |
1450 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "MicroOpBufferSize" , Separator: ','); |
1451 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "LoopMicroOpBufferSize" , Separator: ','); |
1452 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "LoadLatency" , Separator: ','); |
1453 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "HighLatency" , Separator: ','); |
1454 | EmitProcessorProp(OS, R: PM.ModelDef, Name: "MispredictPenalty" , Separator: ','); |
1455 | |
1456 | bool PostRAScheduler = |
1457 | (PM.ModelDef ? PM.ModelDef->getValueAsBit(FieldName: "PostRAScheduler" ) : false); |
1458 | |
1459 | OS << " " << (PostRAScheduler ? "true" : "false" ) << ", // " |
1460 | << "PostRAScheduler\n" ; |
1461 | |
1462 | bool CompleteModel = |
1463 | (PM.ModelDef ? PM.ModelDef->getValueAsBit(FieldName: "CompleteModel" ) : false); |
1464 | |
1465 | OS << " " << (CompleteModel ? "true" : "false" ) << ", // " |
1466 | << "CompleteModel\n" ; |
1467 | |
1468 | bool EnableIntervals = |
1469 | (PM.ModelDef ? PM.ModelDef->getValueAsBit(FieldName: "EnableIntervals" ) : false); |
1470 | |
1471 | OS << " " << (EnableIntervals ? "true" : "false" ) << ", // " |
1472 | << "EnableIntervals\n" ; |
1473 | |
1474 | OS << " " << PM.Index << ", // Processor ID\n" ; |
1475 | if (PM.hasInstrSchedModel()) |
1476 | OS << " " << PM.ModelName << "ProcResources" |
1477 | << ",\n" |
1478 | << " " << PM.ModelName << "SchedClasses" |
1479 | << ",\n" |
1480 | << " " << PM.ProcResourceDefs.size() + 1 << ",\n" |
1481 | << " " |
1482 | << (SchedModels.schedClassEnd() - SchedModels.schedClassBegin()) |
1483 | << ",\n" ; |
1484 | else |
1485 | OS << " nullptr, nullptr, 0, 0," |
1486 | << " // No instruction-level machine model.\n" ; |
1487 | if (PM.hasItineraries()) |
1488 | OS << " " << PM.ItinsDef->getName() << ",\n" ; |
1489 | else |
1490 | OS << " nullptr, // No Itinerary\n" ; |
1491 | if (PM.hasExtraProcessorInfo()) |
1492 | OS << " &" << PM.ModelName << "ExtraInfo,\n" ; |
1493 | else |
1494 | OS << " nullptr // No extra processor descriptor\n" ; |
1495 | OS << "};\n" ; |
1496 | } |
1497 | } |
1498 | |
1499 | // |
1500 | // EmitSchedModel - Emits all scheduling model tables, folding common patterns. |
1501 | // |
1502 | void SubtargetEmitter::EmitSchedModel(raw_ostream &OS) { |
1503 | OS << "#ifdef DBGFIELD\n" |
1504 | << "#error \"<target>GenSubtargetInfo.inc requires a DBGFIELD macro\"\n" |
1505 | << "#endif\n" |
1506 | << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n" |
1507 | << "#define DBGFIELD(x) x,\n" |
1508 | << "#else\n" |
1509 | << "#define DBGFIELD(x)\n" |
1510 | << "#endif\n" ; |
1511 | |
1512 | if (SchedModels.hasItineraries()) { |
1513 | std::vector<std::vector<InstrItinerary>> ProcItinLists; |
1514 | // Emit the stage data |
1515 | EmitStageAndOperandCycleData(OS, ProcItinLists); |
1516 | EmitItineraries(OS, ProcItinLists); |
1517 | } |
1518 | OS << "\n// ===============================================================\n" |
1519 | << "// Data tables for the new per-operand machine model.\n" ; |
1520 | |
1521 | SchedClassTables SchedTables; |
1522 | for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { |
1523 | GenSchedClassTables(ProcModel, SchedTables); |
1524 | } |
1525 | EmitSchedClassTables(SchedTables, OS); |
1526 | |
1527 | OS << "\n#undef DBGFIELD\n" ; |
1528 | |
1529 | // Emit the processor machine model |
1530 | EmitProcessorModels(OS); |
1531 | } |
1532 | |
1533 | static void emitPredicateProlog(const RecordKeeper &Records, raw_ostream &OS) { |
1534 | std::string Buffer; |
1535 | raw_string_ostream Stream(Buffer); |
1536 | |
1537 | // Collect all the PredicateProlog records and print them to the output |
1538 | // stream. |
1539 | std::vector<Record *> Prologs = |
1540 | Records.getAllDerivedDefinitions(ClassName: "PredicateProlog" ); |
1541 | llvm::sort(C&: Prologs, Comp: LessRecord()); |
1542 | for (Record *P : Prologs) |
1543 | Stream << P->getValueAsString(FieldName: "Code" ) << '\n'; |
1544 | |
1545 | OS << Buffer; |
1546 | } |
1547 | |
1548 | static bool isTruePredicate(const Record *Rec) { |
1549 | return Rec->isSubClassOf(Name: "MCSchedPredicate" ) && |
1550 | Rec->getValueAsDef(FieldName: "Pred" )->isSubClassOf(Name: "MCTrue" ); |
1551 | } |
1552 | |
1553 | static void emitPredicates(const CodeGenSchedTransition &T, |
1554 | const CodeGenSchedClass &SC, PredicateExpander &PE, |
1555 | raw_ostream &OS) { |
1556 | std::string Buffer; |
1557 | raw_string_ostream SS(Buffer); |
1558 | |
1559 | // If not all predicates are MCTrue, then we need an if-stmt. |
1560 | unsigned NumNonTruePreds = |
1561 | T.PredTerm.size() - count_if(Range: T.PredTerm, P: isTruePredicate); |
1562 | |
1563 | SS.indent(NumSpaces: PE.getIndentLevel() * 2); |
1564 | |
1565 | if (NumNonTruePreds) { |
1566 | bool FirstNonTruePredicate = true; |
1567 | SS << "if (" ; |
1568 | |
1569 | PE.setIndentLevel(PE.getIndentLevel() + 2); |
1570 | |
1571 | for (const Record *Rec : T.PredTerm) { |
1572 | // Skip predicates that evaluate to "true". |
1573 | if (isTruePredicate(Rec)) |
1574 | continue; |
1575 | |
1576 | if (FirstNonTruePredicate) { |
1577 | FirstNonTruePredicate = false; |
1578 | } else { |
1579 | SS << "\n" ; |
1580 | SS.indent(NumSpaces: PE.getIndentLevel() * 2); |
1581 | SS << "&& " ; |
1582 | } |
1583 | |
1584 | if (Rec->isSubClassOf(Name: "MCSchedPredicate" )) { |
1585 | PE.expandPredicate(OS&: SS, Rec: Rec->getValueAsDef(FieldName: "Pred" )); |
1586 | continue; |
1587 | } |
1588 | |
1589 | // Expand this legacy predicate and wrap it around braces if there is more |
1590 | // than one predicate to expand. |
1591 | SS << ((NumNonTruePreds > 1) ? "(" : "" ) |
1592 | << Rec->getValueAsString(FieldName: "Predicate" ) |
1593 | << ((NumNonTruePreds > 1) ? ")" : "" ); |
1594 | } |
1595 | |
1596 | SS << ")\n" ; // end of if-stmt |
1597 | PE.decreaseIndentLevel(); |
1598 | SS.indent(NumSpaces: PE.getIndentLevel() * 2); |
1599 | PE.decreaseIndentLevel(); |
1600 | } |
1601 | |
1602 | SS << "return " << T.ToClassIdx << "; // " << SC.Name << '\n'; |
1603 | OS << Buffer; |
1604 | } |
1605 | |
1606 | // Used by method `SubtargetEmitter::emitSchedModelHelpersImpl()` to generate |
1607 | // epilogue code for the auto-generated helper. |
1608 | static void emitSchedModelHelperEpilogue(raw_ostream &OS, |
1609 | bool ShouldReturnZero) { |
1610 | if (ShouldReturnZero) { |
1611 | OS << " // Don't know how to resolve this scheduling class.\n" |
1612 | << " return 0;\n" ; |
1613 | return; |
1614 | } |
1615 | |
1616 | OS << " report_fatal_error(\"Expected a variant SchedClass\");\n" ; |
1617 | } |
1618 | |
1619 | static bool hasMCSchedPredicates(const CodeGenSchedTransition &T) { |
1620 | return all_of(Range: T.PredTerm, P: [](const Record *Rec) { |
1621 | return Rec->isSubClassOf(Name: "MCSchedPredicate" ); |
1622 | }); |
1623 | } |
1624 | |
1625 | static void collectVariantClasses(const CodeGenSchedModels &SchedModels, |
1626 | IdxVec &VariantClasses, |
1627 | bool OnlyExpandMCInstPredicates) { |
1628 | for (const CodeGenSchedClass &SC : SchedModels.schedClasses()) { |
1629 | // Ignore non-variant scheduling classes. |
1630 | if (SC.Transitions.empty()) |
1631 | continue; |
1632 | |
1633 | if (OnlyExpandMCInstPredicates) { |
1634 | // Ignore this variant scheduling class no transitions use any meaningful |
1635 | // MCSchedPredicate definitions. |
1636 | if (llvm::none_of(Range: SC.Transitions, P: hasMCSchedPredicates)) |
1637 | continue; |
1638 | } |
1639 | |
1640 | VariantClasses.push_back(x: SC.Index); |
1641 | } |
1642 | } |
1643 | |
1644 | static void collectProcessorIndices(const CodeGenSchedClass &SC, |
1645 | IdxVec &ProcIndices) { |
1646 | // A variant scheduling class may define transitions for multiple |
1647 | // processors. This function identifies wich processors are associated with |
1648 | // transition rules specified by variant class `SC`. |
1649 | for (const CodeGenSchedTransition &T : SC.Transitions) { |
1650 | IdxVec PI; |
1651 | std::set_union(first1: &T.ProcIndex, last1: &T.ProcIndex + 1, first2: ProcIndices.begin(), |
1652 | last2: ProcIndices.end(), result: std::back_inserter(x&: PI)); |
1653 | ProcIndices = std::move(PI); |
1654 | } |
1655 | } |
1656 | |
1657 | static bool isAlwaysTrue(const CodeGenSchedTransition &T) { |
1658 | return llvm::all_of(Range: T.PredTerm, P: isTruePredicate); |
1659 | } |
1660 | |
1661 | void SubtargetEmitter::emitSchedModelHelpersImpl( |
1662 | raw_ostream &OS, bool OnlyExpandMCInstPredicates) { |
1663 | IdxVec VariantClasses; |
1664 | collectVariantClasses(SchedModels, VariantClasses, |
1665 | OnlyExpandMCInstPredicates); |
1666 | |
1667 | if (VariantClasses.empty()) { |
1668 | emitSchedModelHelperEpilogue(OS, ShouldReturnZero: OnlyExpandMCInstPredicates); |
1669 | return; |
1670 | } |
1671 | |
1672 | // Construct a switch statement where the condition is a check on the |
1673 | // scheduling class identifier. There is a `case` for every variant class |
1674 | // defined by the processor models of this target. |
1675 | // Each `case` implements a number of rules to resolve (i.e. to transition |
1676 | // from) a variant scheduling class to another scheduling class. Rules are |
1677 | // described by instances of CodeGenSchedTransition. Note that transitions may |
1678 | // not be valid for all processors. |
1679 | OS << " switch (SchedClass) {\n" ; |
1680 | for (unsigned VC : VariantClasses) { |
1681 | IdxVec ProcIndices; |
1682 | const CodeGenSchedClass &SC = SchedModels.getSchedClass(Idx: VC); |
1683 | collectProcessorIndices(SC, ProcIndices); |
1684 | |
1685 | OS << " case " << VC << ": // " << SC.Name << '\n'; |
1686 | |
1687 | PredicateExpander PE(Target); |
1688 | PE.setByRef(false); |
1689 | PE.setExpandForMC(OnlyExpandMCInstPredicates); |
1690 | for (unsigned PI : ProcIndices) { |
1691 | OS << " " ; |
1692 | |
1693 | // Emit a guard on the processor ID. |
1694 | if (PI != 0) { |
1695 | OS << (OnlyExpandMCInstPredicates |
1696 | ? "if (CPUID == " |
1697 | : "if (SchedModel->getProcessorID() == " ); |
1698 | OS << PI << ") " ; |
1699 | OS << "{ // " << (SchedModels.procModelBegin() + PI)->ModelName << '\n'; |
1700 | } |
1701 | |
1702 | // Now emit transitions associated with processor PI. |
1703 | const CodeGenSchedTransition *FinalT = nullptr; |
1704 | for (const CodeGenSchedTransition &T : SC.Transitions) { |
1705 | if (PI != 0 && T.ProcIndex != PI) |
1706 | continue; |
1707 | |
1708 | // Emit only transitions based on MCSchedPredicate, if it's the case. |
1709 | // At least the transition specified by NoSchedPred is emitted, |
1710 | // which becomes the default transition for those variants otherwise |
1711 | // not based on MCSchedPredicate. |
1712 | // FIXME: preferably, llvm-mca should instead assume a reasonable |
1713 | // default when a variant transition is not based on MCSchedPredicate |
1714 | // for a given processor. |
1715 | if (OnlyExpandMCInstPredicates && !hasMCSchedPredicates(T)) |
1716 | continue; |
1717 | |
1718 | // If transition is folded to 'return X' it should be the last one. |
1719 | if (isAlwaysTrue(T)) { |
1720 | FinalT = &T; |
1721 | continue; |
1722 | } |
1723 | PE.setIndentLevel(3); |
1724 | emitPredicates(T, SC: SchedModels.getSchedClass(Idx: T.ToClassIdx), PE, OS); |
1725 | } |
1726 | if (FinalT) |
1727 | emitPredicates(T: *FinalT, SC: SchedModels.getSchedClass(Idx: FinalT->ToClassIdx), |
1728 | PE, OS); |
1729 | |
1730 | OS << " }\n" ; |
1731 | |
1732 | if (PI == 0) |
1733 | break; |
1734 | } |
1735 | |
1736 | if (SC.isInferred()) |
1737 | OS << " return " << SC.Index << ";\n" ; |
1738 | OS << " break;\n" ; |
1739 | } |
1740 | |
1741 | OS << " };\n" ; |
1742 | |
1743 | emitSchedModelHelperEpilogue(OS, ShouldReturnZero: OnlyExpandMCInstPredicates); |
1744 | } |
1745 | |
1746 | void SubtargetEmitter::EmitSchedModelHelpers(const std::string &ClassName, |
1747 | raw_ostream &OS) { |
1748 | OS << "unsigned " << ClassName |
1749 | << "\n::resolveSchedClass(unsigned SchedClass, const MachineInstr *MI," |
1750 | << " const TargetSchedModel *SchedModel) const {\n" ; |
1751 | |
1752 | // Emit the predicate prolog code. |
1753 | emitPredicateProlog(Records, OS); |
1754 | |
1755 | // Emit target predicates. |
1756 | emitSchedModelHelpersImpl(OS); |
1757 | |
1758 | OS << "} // " << ClassName << "::resolveSchedClass\n\n" ; |
1759 | |
1760 | OS << "unsigned " << ClassName |
1761 | << "\n::resolveVariantSchedClass(unsigned SchedClass, const MCInst *MI," |
1762 | << " const MCInstrInfo *MCII, unsigned CPUID) const {\n" |
1763 | << " return " << Target << "_MC" |
1764 | << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n" |
1765 | << "} // " << ClassName << "::resolveVariantSchedClass\n\n" ; |
1766 | |
1767 | STIPredicateExpander PE(Target); |
1768 | PE.setClassPrefix(ClassName); |
1769 | PE.setExpandDefinition(true); |
1770 | PE.setByRef(false); |
1771 | PE.setIndentLevel(0); |
1772 | |
1773 | for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) |
1774 | PE.expandSTIPredicate(OS, Fn); |
1775 | } |
1776 | |
1777 | void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName, |
1778 | raw_ostream &OS) { |
1779 | const CodeGenHwModes &CGH = TGT.getHwModes(); |
1780 | assert(CGH.getNumModeIds() > 0); |
1781 | if (CGH.getNumModeIds() == 1) |
1782 | return; |
1783 | |
1784 | OS << "unsigned " << ClassName << "::getHwMode() const {\n" ; |
1785 | for (unsigned M = 1, NumModes = CGH.getNumModeIds(); M != NumModes; ++M) { |
1786 | const HwMode &HM = CGH.getMode(Id: M); |
1787 | OS << " if (checkFeatures(\"" << HM.Features << "\")) return " << M |
1788 | << ";\n" ; |
1789 | } |
1790 | OS << " return 0;\n}\n" ; |
1791 | } |
1792 | |
1793 | void SubtargetEmitter::emitGetMacroFusions(const std::string &ClassName, |
1794 | raw_ostream &OS) { |
1795 | if (!TGT.hasMacroFusion()) |
1796 | return; |
1797 | |
1798 | OS << "std::vector<MacroFusionPredTy> " << ClassName |
1799 | << "::getMacroFusions() const {\n" ; |
1800 | OS.indent(NumSpaces: 2) << "std::vector<MacroFusionPredTy> Fusions;\n" ; |
1801 | for (auto *Fusion : TGT.getMacroFusions()) { |
1802 | std::string Name = Fusion->getNameInitAsString(); |
1803 | OS.indent(NumSpaces: 2) << "if (hasFeature(" << Target << "::" << Name |
1804 | << ")) Fusions.push_back(llvm::is" << Name << ");\n" ; |
1805 | } |
1806 | |
1807 | OS.indent(NumSpaces: 2) << "return Fusions;\n" ; |
1808 | OS << "}\n" ; |
1809 | } |
1810 | |
1811 | // Produces a subtarget specific function for parsing |
1812 | // the subtarget features string. |
1813 | void SubtargetEmitter::ParseFeaturesFunction(raw_ostream &OS) { |
1814 | std::vector<Record *> Features = |
1815 | Records.getAllDerivedDefinitions(ClassName: "SubtargetFeature" ); |
1816 | llvm::sort(C&: Features, Comp: LessRecord()); |
1817 | |
1818 | OS << "// ParseSubtargetFeatures - Parses features string setting specified\n" |
1819 | << "// subtarget options.\n" |
1820 | << "void llvm::" ; |
1821 | OS << Target; |
1822 | OS << "Subtarget::ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, " |
1823 | << "StringRef FS) {\n" |
1824 | << " LLVM_DEBUG(dbgs() << \"\\nFeatures:\" << FS);\n" |
1825 | << " LLVM_DEBUG(dbgs() << \"\\nCPU:\" << CPU);\n" |
1826 | << " LLVM_DEBUG(dbgs() << \"\\nTuneCPU:\" << TuneCPU << \"\\n\\n\");\n" ; |
1827 | |
1828 | if (Features.empty()) { |
1829 | OS << "}\n" ; |
1830 | return; |
1831 | } |
1832 | |
1833 | OS << " InitMCProcessorInfo(CPU, TuneCPU, FS);\n" |
1834 | << " const FeatureBitset &Bits = getFeatureBits();\n" ; |
1835 | |
1836 | for (Record *R : Features) { |
1837 | // Next record |
1838 | StringRef Instance = R->getName(); |
1839 | StringRef Value = R->getValueAsString(FieldName: "Value" ); |
1840 | StringRef FieldName = R->getValueAsString(FieldName: "FieldName" ); |
1841 | |
1842 | if (Value == "true" || Value == "false" ) |
1843 | OS << " if (Bits[" << Target << "::" << Instance << "]) " << FieldName |
1844 | << " = " << Value << ";\n" ; |
1845 | else |
1846 | OS << " if (Bits[" << Target << "::" << Instance << "] && " << FieldName |
1847 | << " < " << Value << ") " << FieldName << " = " << Value << ";\n" ; |
1848 | } |
1849 | |
1850 | OS << "}\n" ; |
1851 | } |
1852 | |
1853 | void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) { |
1854 | OS << "namespace " << Target << "_MC {\n" |
1855 | << "unsigned resolveVariantSchedClassImpl(unsigned SchedClass,\n" |
1856 | << " const MCInst *MI, const MCInstrInfo *MCII, unsigned CPUID) {\n" ; |
1857 | emitSchedModelHelpersImpl(OS, /* OnlyExpandMCPredicates */ OnlyExpandMCInstPredicates: true); |
1858 | OS << "}\n" ; |
1859 | OS << "} // end namespace " << Target << "_MC\n\n" ; |
1860 | |
1861 | OS << "struct " << Target |
1862 | << "GenMCSubtargetInfo : public MCSubtargetInfo {\n" ; |
1863 | OS << " " << Target << "GenMCSubtargetInfo(const Triple &TT,\n" |
1864 | << " StringRef CPU, StringRef TuneCPU, StringRef FS,\n" |
1865 | << " ArrayRef<SubtargetFeatureKV> PF,\n" |
1866 | << " ArrayRef<SubtargetSubTypeKV> PD,\n" |
1867 | << " const MCWriteProcResEntry *WPR,\n" |
1868 | << " const MCWriteLatencyEntry *WL,\n" |
1869 | << " const MCReadAdvanceEntry *RA, const InstrStage *IS,\n" |
1870 | << " const unsigned *OC, const unsigned *FP) :\n" |
1871 | << " MCSubtargetInfo(TT, CPU, TuneCPU, FS, PF, PD,\n" |
1872 | << " WPR, WL, RA, IS, OC, FP) { }\n\n" |
1873 | << " unsigned resolveVariantSchedClass(unsigned SchedClass,\n" |
1874 | << " const MCInst *MI, const MCInstrInfo *MCII,\n" |
1875 | << " unsigned CPUID) const override {\n" |
1876 | << " return " << Target << "_MC" |
1877 | << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n" ; |
1878 | OS << " }\n" ; |
1879 | if (TGT.getHwModes().getNumModeIds() > 1) |
1880 | OS << " unsigned getHwMode() const override;\n" ; |
1881 | OS << "};\n" ; |
1882 | EmitHwModeCheck(ClassName: Target + "GenMCSubtargetInfo" , OS); |
1883 | } |
1884 | |
1885 | void SubtargetEmitter::EmitMCInstrAnalysisPredicateFunctions(raw_ostream &OS) { |
1886 | OS << "\n#ifdef GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n" ; |
1887 | OS << "#undef GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n\n" ; |
1888 | |
1889 | STIPredicateExpander PE(Target); |
1890 | PE.setExpandForMC(true); |
1891 | PE.setByRef(true); |
1892 | for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) |
1893 | PE.expandSTIPredicate(OS, Fn); |
1894 | |
1895 | OS << "#endif // GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n\n" ; |
1896 | |
1897 | OS << "\n#ifdef GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n" ; |
1898 | OS << "#undef GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n\n" ; |
1899 | |
1900 | std::string ClassPrefix = Target + "MCInstrAnalysis" ; |
1901 | PE.setExpandDefinition(true); |
1902 | PE.setClassPrefix(ClassPrefix); |
1903 | PE.setIndentLevel(0); |
1904 | for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) |
1905 | PE.expandSTIPredicate(OS, Fn); |
1906 | |
1907 | OS << "#endif // GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n\n" ; |
1908 | } |
1909 | |
1910 | // |
1911 | // SubtargetEmitter::run - Main subtarget enumeration emitter. |
1912 | // |
1913 | void SubtargetEmitter::run(raw_ostream &OS) { |
1914 | emitSourceFileHeader(Desc: "Subtarget Enumeration Source Fragment" , OS); |
1915 | |
1916 | OS << "\n#ifdef GET_SUBTARGETINFO_ENUM\n" ; |
1917 | OS << "#undef GET_SUBTARGETINFO_ENUM\n\n" ; |
1918 | |
1919 | DenseMap<Record *, unsigned> FeatureMap; |
1920 | |
1921 | OS << "namespace llvm {\n" ; |
1922 | Enumeration(OS, FeatureMap); |
1923 | OS << "} // end namespace llvm\n\n" ; |
1924 | OS << "#endif // GET_SUBTARGETINFO_ENUM\n\n" ; |
1925 | |
1926 | EmitSubtargetInfoMacroCalls(OS); |
1927 | |
1928 | OS << "namespace llvm {\n" ; |
1929 | #if 0 |
1930 | OS << "namespace {\n" ; |
1931 | #endif |
1932 | unsigned NumFeatures = FeatureKeyValues(OS, FeatureMap); |
1933 | OS << "\n" ; |
1934 | EmitSchedModel(OS); |
1935 | OS << "\n" ; |
1936 | unsigned NumProcs = CPUKeyValues(OS, FeatureMap); |
1937 | OS << "\n" ; |
1938 | #if 0 |
1939 | OS << "} // end anonymous namespace\n\n" ; |
1940 | #endif |
1941 | |
1942 | // MCInstrInfo initialization routine. |
1943 | emitGenMCSubtargetInfo(OS); |
1944 | |
1945 | OS << "\nstatic inline MCSubtargetInfo *create" << Target |
1946 | << "MCSubtargetInfoImpl(" |
1947 | << "const Triple &TT, StringRef CPU, StringRef TuneCPU, StringRef FS) {\n" ; |
1948 | OS << " return new " << Target |
1949 | << "GenMCSubtargetInfo(TT, CPU, TuneCPU, FS, " ; |
1950 | if (NumFeatures) |
1951 | OS << Target << "FeatureKV, " ; |
1952 | else |
1953 | OS << "std::nullopt, " ; |
1954 | if (NumProcs) |
1955 | OS << Target << "SubTypeKV, " ; |
1956 | else |
1957 | OS << "std::nullopt, " ; |
1958 | OS << '\n'; |
1959 | OS.indent(NumSpaces: 22); |
1960 | OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " |
1961 | << Target << "ReadAdvanceTable, " ; |
1962 | OS << '\n'; |
1963 | OS.indent(NumSpaces: 22); |
1964 | if (SchedModels.hasItineraries()) { |
1965 | OS << Target << "Stages, " << Target << "OperandCycles, " << Target |
1966 | << "ForwardingPaths" ; |
1967 | } else |
1968 | OS << "nullptr, nullptr, nullptr" ; |
1969 | OS << ");\n}\n\n" ; |
1970 | |
1971 | OS << "} // end namespace llvm\n\n" ; |
1972 | |
1973 | OS << "#endif // GET_SUBTARGETINFO_MC_DESC\n\n" ; |
1974 | |
1975 | OS << "\n#ifdef GET_SUBTARGETINFO_TARGET_DESC\n" ; |
1976 | OS << "#undef GET_SUBTARGETINFO_TARGET_DESC\n\n" ; |
1977 | |
1978 | OS << "#include \"llvm/Support/Debug.h\"\n" ; |
1979 | OS << "#include \"llvm/Support/raw_ostream.h\"\n\n" ; |
1980 | ParseFeaturesFunction(OS); |
1981 | |
1982 | OS << "#endif // GET_SUBTARGETINFO_TARGET_DESC\n\n" ; |
1983 | |
1984 | // Create a TargetSubtargetInfo subclass to hide the MC layer initialization. |
1985 | OS << "\n#ifdef GET_SUBTARGETINFO_HEADER\n" ; |
1986 | OS << "#undef GET_SUBTARGETINFO_HEADER\n\n" ; |
1987 | |
1988 | std::string ClassName = Target + "GenSubtargetInfo" ; |
1989 | OS << "namespace llvm {\n" ; |
1990 | OS << "class DFAPacketizer;\n" ; |
1991 | OS << "namespace " << Target << "_MC {\n" |
1992 | << "unsigned resolveVariantSchedClassImpl(unsigned SchedClass," |
1993 | << " const MCInst *MI, const MCInstrInfo *MCII, unsigned CPUID);\n" |
1994 | << "} // end namespace " << Target << "_MC\n\n" ; |
1995 | OS << "struct " << ClassName << " : public TargetSubtargetInfo {\n" |
1996 | << " explicit " << ClassName << "(const Triple &TT, StringRef CPU, " |
1997 | << "StringRef TuneCPU, StringRef FS);\n" |
1998 | << "public:\n" |
1999 | << " unsigned resolveSchedClass(unsigned SchedClass, " |
2000 | << " const MachineInstr *DefMI," |
2001 | << " const TargetSchedModel *SchedModel) const override;\n" |
2002 | << " unsigned resolveVariantSchedClass(unsigned SchedClass," |
2003 | << " const MCInst *MI, const MCInstrInfo *MCII," |
2004 | << " unsigned CPUID) const override;\n" |
2005 | << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" |
2006 | << " const;\n" ; |
2007 | if (TGT.getHwModes().getNumModeIds() > 1) |
2008 | OS << " unsigned getHwMode() const override;\n" ; |
2009 | if (TGT.hasMacroFusion()) |
2010 | OS << " std::vector<MacroFusionPredTy> getMacroFusions() const " |
2011 | "override;\n" ; |
2012 | |
2013 | STIPredicateExpander PE(Target); |
2014 | PE.setByRef(false); |
2015 | for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) |
2016 | PE.expandSTIPredicate(OS, Fn); |
2017 | |
2018 | OS << "};\n" |
2019 | << "} // end namespace llvm\n\n" ; |
2020 | |
2021 | OS << "#endif // GET_SUBTARGETINFO_HEADER\n\n" ; |
2022 | |
2023 | OS << "\n#ifdef GET_SUBTARGETINFO_CTOR\n" ; |
2024 | OS << "#undef GET_SUBTARGETINFO_CTOR\n\n" ; |
2025 | |
2026 | OS << "#include \"llvm/CodeGen/TargetSchedule.h\"\n\n" ; |
2027 | OS << "namespace llvm {\n" ; |
2028 | OS << "extern const llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n" ; |
2029 | OS << "extern const llvm::SubtargetSubTypeKV " << Target << "SubTypeKV[];\n" ; |
2030 | OS << "extern const llvm::MCWriteProcResEntry " << Target |
2031 | << "WriteProcResTable[];\n" ; |
2032 | OS << "extern const llvm::MCWriteLatencyEntry " << Target |
2033 | << "WriteLatencyTable[];\n" ; |
2034 | OS << "extern const llvm::MCReadAdvanceEntry " << Target |
2035 | << "ReadAdvanceTable[];\n" ; |
2036 | |
2037 | if (SchedModels.hasItineraries()) { |
2038 | OS << "extern const llvm::InstrStage " << Target << "Stages[];\n" ; |
2039 | OS << "extern const unsigned " << Target << "OperandCycles[];\n" ; |
2040 | OS << "extern const unsigned " << Target << "ForwardingPaths[];\n" ; |
2041 | } |
2042 | |
2043 | OS << ClassName << "::" << ClassName << "(const Triple &TT, StringRef CPU, " |
2044 | << "StringRef TuneCPU, StringRef FS)\n" |
2045 | << " : TargetSubtargetInfo(TT, CPU, TuneCPU, FS, " ; |
2046 | if (NumFeatures) |
2047 | OS << "ArrayRef(" << Target << "FeatureKV, " << NumFeatures << "), " ; |
2048 | else |
2049 | OS << "std::nullopt, " ; |
2050 | if (NumProcs) |
2051 | OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), " ; |
2052 | else |
2053 | OS << "std::nullopt, " ; |
2054 | OS << '\n'; |
2055 | OS.indent(NumSpaces: 24); |
2056 | OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " |
2057 | << Target << "ReadAdvanceTable, " ; |
2058 | OS << '\n'; |
2059 | OS.indent(NumSpaces: 24); |
2060 | if (SchedModels.hasItineraries()) { |
2061 | OS << Target << "Stages, " << Target << "OperandCycles, " << Target |
2062 | << "ForwardingPaths" ; |
2063 | } else |
2064 | OS << "nullptr, nullptr, nullptr" ; |
2065 | OS << ") {}\n\n" ; |
2066 | |
2067 | EmitSchedModelHelpers(ClassName, OS); |
2068 | EmitHwModeCheck(ClassName, OS); |
2069 | emitGetMacroFusions(ClassName, OS); |
2070 | |
2071 | OS << "} // end namespace llvm\n\n" ; |
2072 | |
2073 | OS << "#endif // GET_SUBTARGETINFO_CTOR\n\n" ; |
2074 | |
2075 | EmitMCInstrAnalysisPredicateFunctions(OS); |
2076 | } |
2077 | |
2078 | static TableGen::Emitter::OptClass<SubtargetEmitter> |
2079 | X("gen-subtarget" , "Generate subtarget enumerations" ); |
2080 | |