1//===- RISCVTargetDefEmitter.cpp - Generate lists of RISC-V CPUs ----------===//
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 the include file needed by RISCVTargetParser.cpp
10// and RISCVISAInfo.cpp to parse the RISC-V CPUs and extensions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/DenseSet.h"
15#include "llvm/Support/Format.h"
16#include "llvm/Support/FormatVariadic.h"
17#include "llvm/Support/RISCVISAUtils.h"
18#include "llvm/TableGen/Error.h"
19#include "llvm/TableGen/Record.h"
20#include "llvm/TableGen/StringToOffsetTable.h"
21#include "llvm/TableGen/TableGenBackend.h"
22
23using namespace llvm;
24
25static StringRef getExtensionName(const Record *R) {
26 StringRef Name = R->getValueAsString(FieldName: "Name");
27 Name.consume_front(Prefix: "experimental-");
28 return Name;
29}
30
31static void printExtensionTable(raw_ostream &OS,
32 ArrayRef<const Record *> Extensions,
33 bool Experimental) {
34 OS << "static const RISCVSupportedExtension Supported";
35 if (Experimental)
36 OS << "Experimental";
37 OS << "Extensions[] = {\n";
38
39 for (const Record *R : Extensions) {
40 if (R->getValueAsBit(FieldName: "Experimental") != Experimental)
41 continue;
42
43 OS.indent(NumSpaces: 4) << "{\"" << getExtensionName(R) << "\", {"
44 << R->getValueAsInt(FieldName: "MajorVersion") << ", "
45 << R->getValueAsInt(FieldName: "MinorVersion") << "}},\n";
46 }
47
48 OS << "};\n\n";
49}
50
51static void emitRISCVExtensions(const RecordKeeper &Records, raw_ostream &OS) {
52 OS << "#ifdef GET_SUPPORTED_EXTENSIONS\n";
53 OS << "#undef GET_SUPPORTED_EXTENSIONS\n\n";
54
55 std::vector<const Record *> Extensions =
56 Records.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVExtension");
57 llvm::sort(C&: Extensions, Comp: [](const Record *Rec1, const Record *Rec2) {
58 return getExtensionName(R: Rec1) < getExtensionName(R: Rec2);
59 });
60
61 if (!Extensions.empty()) {
62 printExtensionTable(OS, Extensions, /*Experimental=*/false);
63 printExtensionTable(OS, Extensions, /*Experimental=*/true);
64 }
65
66 OS << "#endif // GET_SUPPORTED_EXTENSIONS\n\n";
67
68 OS << "#ifdef GET_IMPLIED_EXTENSIONS\n";
69 OS << "#undef GET_IMPLIED_EXTENSIONS\n\n";
70
71 if (!Extensions.empty()) {
72 OS << "\nstatic constexpr ImpliedExtsEntry ImpliedExts[] = {\n";
73 for (const Record *Ext : Extensions) {
74 std::vector<const Record *> ImpliesList =
75 Ext->getValueAsListOfDefs(FieldName: "Implies");
76 if (ImpliesList.empty())
77 continue;
78
79 StringRef Name = getExtensionName(R: Ext);
80
81 for (const Record *ImpliedExt : ImpliesList) {
82 if (!ImpliedExt->isSubClassOf(Name: "RISCVExtension"))
83 continue;
84
85 OS.indent(NumSpaces: 4) << "{ {\"" << Name << "\"}, \""
86 << getExtensionName(R: ImpliedExt) << "\"},\n";
87 }
88 }
89
90 OS << "};\n\n";
91 }
92
93 OS << "#endif // GET_IMPLIED_EXTENSIONS\n\n";
94}
95
96// We can generate march string from target features as what has been described
97// in RISC-V ISA specification (version 20191213) 'Chapter 27. ISA Extension
98// Naming Conventions'.
99//
100// This is almost the same as RISCVFeatures::parseFeatureBits, except that we
101// get feature name from feature records instead of feature bits.
102static void printMArch(raw_ostream &OS, ArrayRef<const Record *> Features) {
103 RISCVISAUtils::OrderedExtensionMap Extensions;
104 unsigned XLen = 0;
105
106 // Convert features to FeatureVector.
107 for (const Record *Feature : Features) {
108 StringRef FeatureName = getExtensionName(R: Feature);
109 if (Feature->isSubClassOf(Name: "RISCVExtension")) {
110 unsigned Major = Feature->getValueAsInt(FieldName: "MajorVersion");
111 unsigned Minor = Feature->getValueAsInt(FieldName: "MinorVersion");
112 Extensions[FeatureName.str()] = {.Major: Major, .Minor: Minor};
113 } else if (FeatureName == "64bit") {
114 assert(XLen == 0 && "Already determined XLen");
115 XLen = 64;
116 } else if (FeatureName == "32bit") {
117 assert(XLen == 0 && "Already determined XLen");
118 XLen = 32;
119 }
120 }
121
122 assert(XLen != 0 && "Unable to determine XLen");
123
124 OS << "rv" << XLen;
125
126 ListSeparator LS("_");
127 for (auto const &Ext : Extensions)
128 OS << LS << Ext.first << Ext.second.Major << 'p' << Ext.second.Minor;
129}
130
131static void printProfileTable(raw_ostream &OS,
132 ArrayRef<const Record *> Profiles,
133 bool Experimental) {
134 OS << "static constexpr RISCVProfile Supported";
135 if (Experimental)
136 OS << "Experimental";
137 OS << "Profiles[] = {\n";
138
139 for (const Record *Rec : Profiles) {
140 if (Rec->getValueAsBit(FieldName: "Experimental") != Experimental)
141 continue;
142
143 StringRef Name = Rec->getValueAsString(FieldName: "Name");
144 Name.consume_front(Prefix: "experimental-");
145 OS.indent(NumSpaces: 4) << "{\"" << Name << "\",\"";
146 printMArch(OS, Features: Rec->getValueAsListOfDefs(FieldName: "Implies"));
147 OS << "\"},\n";
148 }
149
150 OS << "};\n\n";
151}
152
153static void emitRISCVProfiles(const RecordKeeper &Records, raw_ostream &OS) {
154 OS << "#ifdef GET_SUPPORTED_PROFILES\n";
155 OS << "#undef GET_SUPPORTED_PROFILES\n\n";
156
157 ArrayRef<const Record *> Profiles =
158 Records.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVProfile");
159
160 if (!Profiles.empty()) {
161 printProfileTable(OS, Profiles, /*Experimental=*/false);
162 bool HasExperimentalProfiles = any_of(Range&: Profiles, P: [&](const Record *Rec) {
163 return Rec->getValueAsBit(FieldName: "Experimental");
164 });
165 if (HasExperimentalProfiles)
166 printProfileTable(OS, Profiles, /*Experimental=*/true);
167 }
168
169 OS << "#endif // GET_SUPPORTED_PROFILES\n\n";
170}
171
172static void emitRISCVProcs(const RecordKeeper &RK, raw_ostream &OS) {
173 OS << "#ifndef PROC\n"
174 << "#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN"
175 << ", FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)\n"
176 << "#endif\n\n";
177
178 // Iterate on all definition records.
179 for (const Record *Rec :
180 RK.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVProcessorModel")) {
181 std::vector<const Record *> Features =
182 Rec->getValueAsListOfDefs(FieldName: "Features");
183 bool FastScalarUnalignedAccess =
184 any_of(Range&: Features, P: [&](const Record *Feature) {
185 return Feature->getValueAsString(FieldName: "Name") == "unaligned-scalar-mem";
186 });
187
188 bool FastVectorUnalignedAccess =
189 any_of(Range&: Features, P: [&](const Record *Feature) {
190 return Feature->getValueAsString(FieldName: "Name") == "unaligned-vector-mem";
191 });
192
193 OS << "PROC(" << Rec->getName() << ", {\"" << Rec->getValueAsString(FieldName: "Name")
194 << "\"}, {\"";
195
196 StringRef MArch = Rec->getValueAsString(FieldName: "DefaultMarch");
197
198 // Compute MArch from features if we don't specify it.
199 if (MArch.empty())
200 printMArch(OS, Features);
201 else
202 OS << MArch;
203
204 uint32_t MVendorID = Rec->getValueAsInt(FieldName: "MVendorID");
205 uint64_t MArchID = Rec->getValueAsInt(FieldName: "MArchID");
206 uint64_t MImpID = Rec->getValueAsInt(FieldName: "MImpID");
207
208 OS << "\"}, " << FastScalarUnalignedAccess << ", "
209 << FastVectorUnalignedAccess;
210 OS << ", " << format_hex(N: MVendorID, Width: 10);
211 OS << ", " << format_hex(N: MArchID, Width: 18);
212 OS << ", " << format_hex(N: MImpID, Width: 18);
213 OS << ")\n";
214 }
215 OS << "\n#undef PROC\n";
216 OS << "\n";
217 OS << "#ifndef TUNE_PROC\n"
218 << "#define TUNE_PROC(ENUM, NAME)\n"
219 << "#endif\n\n";
220
221 for (const Record *Rec :
222 RK.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVTuneProcessorModel")) {
223 OS << "TUNE_PROC(" << Rec->getName() << ", "
224 << "\"" << Rec->getValueAsString(FieldName: "Name") << "\")\n";
225 }
226
227 OS << "\n#undef TUNE_PROC\n";
228}
229
230static void emitRISCVExtensionBitmask(const RecordKeeper &RK, raw_ostream &OS) {
231 std::vector<const Record *> Extensions =
232 RK.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVExtensionBitmask");
233 llvm::sort(C&: Extensions, Comp: [](const Record *Rec1, const Record *Rec2) {
234 unsigned GroupID1 = Rec1->getValueAsInt(FieldName: "GroupID");
235 unsigned GroupID2 = Rec2->getValueAsInt(FieldName: "GroupID");
236 if (GroupID1 != GroupID2)
237 return GroupID1 < GroupID2;
238
239 return Rec1->getValueAsInt(FieldName: "BitPos") < Rec2->getValueAsInt(FieldName: "BitPos");
240 });
241
242#ifndef NDEBUG
243 llvm::DenseSet<std::pair<uint64_t, uint64_t>> Seen;
244#endif
245
246 OS << "#ifdef GET_RISCVExtensionBitmaskTable_IMPL\n";
247 OS << "static const RISCVExtensionBitmask ExtensionBitmask[]={\n";
248 for (const Record *Rec : Extensions) {
249 unsigned GroupIDVal = Rec->getValueAsInt(FieldName: "GroupID");
250 unsigned BitPosVal = Rec->getValueAsInt(FieldName: "BitPos");
251
252 StringRef ExtName = Rec->getValueAsString(FieldName: "Name");
253 ExtName.consume_front(Prefix: "experimental-");
254
255#ifndef NDEBUG
256 assert(Seen.insert({GroupIDVal, BitPosVal}).second && "duplicated bitmask");
257#endif
258
259 OS.indent(NumSpaces: 4) << "{"
260 << "\"" << ExtName << "\""
261 << ", " << GroupIDVal << ", " << BitPosVal << "ULL"
262 << "},\n";
263 }
264 OS << "};\n";
265 OS << "#endif\n\n";
266}
267
268static void emitRISCVTuneFeatures(const RecordKeeper &RK,
269 StringToOffsetTable &StrTable,
270 raw_ostream &OS) {
271 std::vector<const Record *> TuneFeatureRecords =
272 RK.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVTuneFeature");
273
274 // {Post Directive Idx, Neg Directive Idx, TuneFeature Record}
275 SmallVector<std::tuple<unsigned, unsigned, const Record *>>
276 TuneFeatureDirectives;
277 // {Directive Idx -> Original Record}
278 // This is primarily for diagnosing purposes -- when there is a duplication,
279 // we are able to pointed out the previous definition.
280 DenseMap<unsigned, const Record *> DirectiveToRecord;
281 // A list of {Feature Name, Implied Feature Name}
282 SmallVector<std::pair<StringRef, StringRef>> ImpliedFeatureList;
283
284 for (const auto *R : TuneFeatureRecords) {
285 // Preemptively insert feature name into the string table because we know
286 // it will be used later.
287 StringRef FeatureName = R->getValueAsString(FieldName: "Name");
288 StrTable.GetOrAddStringOffset(Str: FeatureName);
289
290 StringRef PosName = R->getValueAsString(FieldName: "PositiveDirectiveName");
291 StringRef NegName = R->getValueAsString(FieldName: "NegativeDirectiveName");
292 unsigned PosIdx = StrTable.GetOrAddStringOffset(Str: PosName);
293 if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(Key: PosIdx, Args&: R);
294 !Inserted) {
295 PrintError(Rec: R, Msg: "RISC-V tune feature positive directive '" +
296 Twine(PosName) + "' was already defined");
297 PrintFatalNote(Rec: ItEntry->second, Msg: "Previously defined here");
298 }
299 unsigned NegIdx = StrTable.GetOrAddStringOffset(Str: NegName);
300 if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(Key: NegIdx, Args&: R);
301 !Inserted) {
302 PrintError(Rec: R, Msg: "RISC-V tune feature negative directive '" +
303 Twine(NegName) + "' was already defined");
304 PrintFatalNote(Rec: ItEntry->second, Msg: "Previously defined here");
305 }
306
307 TuneFeatureDirectives.emplace_back(Args&: PosIdx, Args&: NegIdx, Args&: R);
308 }
309
310 for (const auto *R : TuneFeatureRecords) {
311 std::vector<const Record *> Implies = R->getValueAsListOfDefs(FieldName: "Implies");
312 for (const auto *ImpliedRecord : Implies) {
313 StringRef CurrFeatureName = R->getValueAsString(FieldName: "Name");
314 StringRef ImpliedFeatureName = ImpliedRecord->getValueAsString(FieldName: "Name");
315
316 ImpliedFeatureList.emplace_back(Args&: CurrFeatureName, Args&: ImpliedFeatureName);
317 }
318 }
319
320 OS << "#ifdef GET_TUNE_FEATURES\n";
321 OS << "#undef GET_TUNE_FEATURES\n\n";
322
323 StrTable.EmitStringTableDef(OS, Name: "TuneFeatureStrings");
324 OS << "\n";
325
326 OS << "static constexpr RISCVTuneFeature TuneFeatures[] = {\n";
327 for (const auto &[PosIdx, NegIdx, R] : TuneFeatureDirectives) {
328 StringRef FeatureName = R->getValueAsString(FieldName: "Name");
329 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {0}, {1}, {2} },\t// '{3}'\n", Vals: PosIdx, Vals: NegIdx,
330 Vals: *StrTable.GetStringOffset(Str: FeatureName),
331 Vals&: FeatureName);
332 }
333 OS << "};\n\n";
334
335 OS << "static constexpr RISCVImpliedTuneFeature ImpliedTuneFeatures[] = {\n";
336 for (auto [Feature, ImpliedFeature] : ImpliedFeatureList)
337 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {0}, {1} }, // '{2}' -> '{3}'\n",
338 Vals: *StrTable.GetStringOffset(Str: Feature),
339 Vals: *StrTable.GetStringOffset(Str: ImpliedFeature), Vals&: Feature,
340 Vals&: ImpliedFeature);
341 OS << "};\n\n";
342
343 OS << "#endif // GET_TUNE_FEATURES\n\n";
344}
345
346static void
347emitRISCVConfigurableTuneFeatures(const RecordKeeper &RK,
348 const StringToOffsetTable &StrTable,
349 raw_ostream &OS) {
350 std::vector<const Record *> AllProcModels =
351 RK.getAllDerivedDefinitionsIfDefined(ClassName: "ProcessorModel");
352
353 OS << "#ifdef GET_CONFIGURABLE_TUNE_FEATURES\n";
354 OS << "#undef GET_CONFIGURABLE_TUNE_FEATURES\n\n";
355
356 OS << "static constexpr RISCVConfigurableTuneFeatures "
357 "ConfigurableTuneFeatures[] = {\n";
358
359 for (const Record *Proc : AllProcModels) {
360 StringRef ProcName = Proc->getValueAsString(FieldName: "Name");
361 std::vector<const Record *> TuneFeatures =
362 Proc->getValueAsListOfDefs(FieldName: "ConfigurableTuneFeatures");
363 for (const Record *TF : TuneFeatures) {
364 unsigned PosDirectiveIdx = *StrTable.GetStringOffset(
365 Str: TF->getValueAsString(FieldName: "PositiveDirectiveName"));
366 unsigned NegDirectiveIdx = *StrTable.GetStringOffset(
367 Str: TF->getValueAsString(FieldName: "NegativeDirectiveName"));
368 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {{ \"{0}\" }, {1} },\n", Vals&: ProcName,
369 Vals&: PosDirectiveIdx);
370 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {{ \"{0}\" }, {1} },\n", Vals&: ProcName,
371 Vals&: NegDirectiveIdx);
372 }
373 }
374
375 OS << "};\n\n";
376 OS << "#endif // GET_CONFIGURABLE_TUNE_FEATURES\n";
377}
378
379static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
380 emitRISCVExtensions(Records: RK, OS);
381 emitRISCVProfiles(Records: RK, OS);
382 emitRISCVProcs(RK, OS);
383 emitRISCVExtensionBitmask(RK, OS);
384
385 StringToOffsetTable TuneFeatureStrTable;
386 emitRISCVTuneFeatures(RK, StrTable&: TuneFeatureStrTable, OS);
387 emitRISCVConfigurableTuneFeatures(RK, StrTable: TuneFeatureStrTable, OS);
388}
389
390static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef,
391 "Generate the list of CPUs and extensions for "
392 "RISC-V");
393