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/CodeGenHelpers.h"
19#include "llvm/TableGen/Error.h"
20#include "llvm/TableGen/Record.h"
21#include "llvm/TableGen/StringToOffsetTable.h"
22#include "llvm/TableGen/TableGenBackend.h"
23
24using namespace llvm;
25
26static StringRef getExtensionName(const Record *R) {
27 StringRef Name = R->getValueAsString(FieldName: "Name");
28 Name.consume_front(Prefix: "experimental-");
29 return Name;
30}
31
32static void printExtensionTable(raw_ostream &OS,
33 ArrayRef<const Record *> Extensions,
34 bool Experimental) {
35 OS << "static const RISCVSupportedExtension Supported";
36 if (Experimental)
37 OS << "Experimental";
38 OS << "Extensions[] = {\n";
39
40 for (const Record *R : Extensions) {
41 if (R->getValueAsBit(FieldName: "Experimental") != Experimental)
42 continue;
43
44 OS.indent(NumSpaces: 4) << "{\"" << getExtensionName(R) << "\", {"
45 << R->getValueAsInt(FieldName: "MajorVersion") << ", "
46 << R->getValueAsInt(FieldName: "MinorVersion") << "}},\n";
47 }
48
49 OS << "};\n";
50}
51
52static void emitExtensionTable(ArrayRef<const Record *> Extensions,
53 raw_ostream &OS) {
54 IfDefEmitter IfDef(OS, "GET_SUPPORTED_EXTENSIONS");
55 if (!Extensions.empty()) {
56 printExtensionTable(OS, Extensions, /*Experimental=*/false);
57 printExtensionTable(OS, Extensions, /*Experimental=*/true);
58 }
59}
60
61static void emitImpliedExtensionTable(ArrayRef<const Record *> Extensions,
62 raw_ostream &OS) {
63 IfDefEmitter IfDef(OS, "GET_IMPLIED_EXTENSIONS");
64
65 if (!Extensions.empty()) {
66 OS << "\nstatic constexpr ImpliedExtsEntry ImpliedExts[] = {\n";
67 for (const Record *Ext : Extensions) {
68 std::vector<const Record *> ImpliesList =
69 Ext->getValueAsListOfDefs(FieldName: "Implies");
70 if (ImpliesList.empty())
71 continue;
72
73 StringRef Name = getExtensionName(R: Ext);
74
75 for (const Record *ImpliedExt : ImpliesList) {
76 if (!ImpliedExt->isSubClassOf(Name: "RISCVExtension"))
77 continue;
78
79 OS.indent(NumSpaces: 4) << "{ {\"" << Name << "\"}, \""
80 << getExtensionName(R: ImpliedExt) << "\"},\n";
81 }
82 }
83
84 OS << "};\n";
85 }
86}
87
88static void emitRISCVExtensions(const RecordKeeper &Records, raw_ostream &OS) {
89 std::vector<const Record *> Extensions =
90 Records.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVExtension");
91 llvm::sort(C&: Extensions, Comp: [](const Record *Rec1, const Record *Rec2) {
92 return getExtensionName(R: Rec1) < getExtensionName(R: Rec2);
93 });
94
95 emitExtensionTable(Extensions, OS);
96 emitImpliedExtensionTable(Extensions, OS);
97}
98
99// We can generate march string from target features as what has been described
100// in RISC-V ISA specification (version 20191213) 'Chapter 27. ISA Extension
101// Naming Conventions'.
102//
103// This is almost the same as RISCVFeatures::parseFeatureBits, except that we
104// get feature name from feature records instead of feature bits.
105static void printMArch(raw_ostream &OS, ArrayRef<const Record *> Features) {
106 RISCVISAUtils::OrderedExtensionMap Extensions;
107 unsigned XLen = 0;
108
109 // Convert features to FeatureVector.
110 for (const Record *Feature : Features) {
111 StringRef FeatureName = getExtensionName(R: Feature);
112 if (Feature->isSubClassOf(Name: "RISCVExtension")) {
113 unsigned Major = Feature->getValueAsInt(FieldName: "MajorVersion");
114 unsigned Minor = Feature->getValueAsInt(FieldName: "MinorVersion");
115 Extensions[FeatureName.str()] = {.Major: Major, .Minor: Minor};
116 } else if (FeatureName == "64bit") {
117 assert(XLen == 0 && "Already determined XLen");
118 XLen = 64;
119 } else if (FeatureName == "32bit") {
120 assert(XLen == 0 && "Already determined XLen");
121 XLen = 32;
122 }
123 }
124
125 assert(XLen != 0 && "Unable to determine XLen");
126
127 OS << "rv" << XLen;
128
129 ListSeparator LS("_");
130 for (auto const &Ext : Extensions)
131 OS << LS << Ext.first << Ext.second.Major << 'p' << Ext.second.Minor;
132}
133
134static void printProfileTable(raw_ostream &OS,
135 ArrayRef<const Record *> Profiles,
136 bool Experimental) {
137 OS << "static constexpr RISCVProfile Supported";
138 if (Experimental)
139 OS << "Experimental";
140 OS << "Profiles[] = {\n";
141
142 for (const Record *Rec : Profiles) {
143 if (Rec->getValueAsBit(FieldName: "Experimental") != Experimental)
144 continue;
145
146 StringRef Name = Rec->getValueAsString(FieldName: "Name");
147 Name.consume_front(Prefix: "experimental-");
148 OS.indent(NumSpaces: 4) << "{\"" << Name << "\",\"";
149 printMArch(OS, Features: Rec->getValueAsListOfDefs(FieldName: "Implies"));
150 OS << "\"},\n";
151 }
152
153 OS << "};\n";
154}
155
156static void emitRISCVProfiles(const RecordKeeper &Records, raw_ostream &OS) {
157 IfDefEmitter IfDef(OS, "GET_SUPPORTED_PROFILES");
158
159 ArrayRef<const Record *> Profiles =
160 Records.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVProfile");
161
162 if (!Profiles.empty()) {
163 printProfileTable(OS, Profiles, /*Experimental=*/false);
164 bool HasExperimentalProfiles = any_of(Range&: Profiles, P: [&](const Record *Rec) {
165 return Rec->getValueAsBit(FieldName: "Experimental");
166 });
167 if (HasExperimentalProfiles)
168 printProfileTable(OS, Profiles, /*Experimental=*/true);
169 }
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 IfDefEmitter IfDef(OS, "GET_RISCVExtensionBitmaskTable_IMPL", true);
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}
266
267static void emitRISCVTuneFeatures(const RecordKeeper &RK,
268 StringToOffsetTable &StrTable,
269 raw_ostream &OS) {
270 std::vector<const Record *> TuneFeatureRecords =
271 RK.getAllDerivedDefinitionsIfDefined(ClassName: "RISCVTuneFeature");
272
273 // {Post Directive Idx, Neg Directive Idx, TuneFeature Record}
274 SmallVector<std::tuple<unsigned, unsigned, const Record *>>
275 TuneFeatureDirectives;
276 // {Directive Idx -> Original Record}
277 // This is primarily for diagnosing purposes -- when there is a duplication,
278 // we are able to pointed out the previous definition.
279 DenseMap<unsigned, const Record *> DirectiveToRecord;
280 // A list of {Feature Name, Implied Feature Name}
281 SmallVector<std::pair<StringRef, StringRef>> ImpliedFeatureList;
282
283 for (const auto *R : TuneFeatureRecords) {
284 // Preemptively insert feature name into the string table because we know
285 // it will be used later.
286 StringRef FeatureName = R->getValueAsString(FieldName: "Name");
287 StrTable.GetOrAddStringOffset(Str: FeatureName);
288
289 StringRef PosName = R->getValueAsString(FieldName: "PositiveDirectiveName");
290 StringRef NegName = R->getValueAsString(FieldName: "NegativeDirectiveName");
291 unsigned PosIdx = StrTable.GetOrAddStringOffset(Str: PosName);
292 if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(Key: PosIdx, Args&: R);
293 !Inserted) {
294 PrintError(Rec: R, Msg: "RISC-V tune feature positive directive '" +
295 Twine(PosName) + "' was already defined");
296 PrintFatalNote(Rec: ItEntry->second, Msg: "Previously defined here");
297 }
298 unsigned NegIdx = StrTable.GetOrAddStringOffset(Str: NegName);
299 if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(Key: NegIdx, Args&: R);
300 !Inserted) {
301 PrintError(Rec: R, Msg: "RISC-V tune feature negative directive '" +
302 Twine(NegName) + "' was already defined");
303 PrintFatalNote(Rec: ItEntry->second, Msg: "Previously defined here");
304 }
305
306 TuneFeatureDirectives.emplace_back(Args&: PosIdx, Args&: NegIdx, Args&: R);
307 }
308
309 for (const auto *R : TuneFeatureRecords) {
310 std::vector<const Record *> Implies = R->getValueAsListOfDefs(FieldName: "Implies");
311 for (const auto *ImpliedRecord : Implies) {
312 StringRef CurrFeatureName = R->getValueAsString(FieldName: "Name");
313 StringRef ImpliedFeatureName = ImpliedRecord->getValueAsString(FieldName: "Name");
314
315 ImpliedFeatureList.emplace_back(Args&: CurrFeatureName, Args&: ImpliedFeatureName);
316 }
317 }
318
319 IfDefEmitter IfDef(OS, "GET_TUNE_FEATURES");
320
321 StrTable.EmitStringTableDef(OS, Name: "TuneFeatureStrings");
322 OS << "\n";
323
324 OS << "static constexpr RISCVTuneFeature TuneFeatures[] = {\n";
325 for (const auto &[PosIdx, NegIdx, R] : TuneFeatureDirectives) {
326 StringRef FeatureName = R->getValueAsString(FieldName: "Name");
327 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {0}, {1}, {2} },\t// '{3}'\n", Vals: PosIdx, Vals: NegIdx,
328 Vals: *StrTable.GetStringOffset(Str: FeatureName),
329 Vals&: FeatureName);
330 }
331 OS << "};\n\n";
332
333 OS << "static constexpr RISCVImpliedTuneFeature ImpliedTuneFeatures[] = {\n";
334 for (auto [Feature, ImpliedFeature] : ImpliedFeatureList)
335 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {0}, {1} }, // '{2}' -> '{3}'\n",
336 Vals: *StrTable.GetStringOffset(Str: Feature),
337 Vals: *StrTable.GetStringOffset(Str: ImpliedFeature), Vals&: Feature,
338 Vals&: ImpliedFeature);
339 OS << "};\n";
340}
341
342static void
343emitRISCVConfigurableTuneFeatures(const RecordKeeper &RK,
344 const StringToOffsetTable &StrTable,
345 raw_ostream &OS) {
346 std::vector<const Record *> AllProcModels =
347 RK.getAllDerivedDefinitionsIfDefined(ClassName: "ProcessorModel");
348
349 IfDefEmitter IfDef(OS, "GET_CONFIGURABLE_TUNE_FEATURES");
350
351 OS << "static constexpr RISCVConfigurableTuneFeatures "
352 "ConfigurableTuneFeatures[] = {\n";
353
354 for (const Record *Proc : AllProcModels) {
355 StringRef ProcName = Proc->getValueAsString(FieldName: "Name");
356 std::vector<const Record *> TuneFeatures =
357 Proc->getValueAsListOfDefs(FieldName: "ConfigurableTuneFeatures");
358 for (const Record *TF : TuneFeatures) {
359 unsigned PosDirectiveIdx = *StrTable.GetStringOffset(
360 Str: TF->getValueAsString(FieldName: "PositiveDirectiveName"));
361 unsigned NegDirectiveIdx = *StrTable.GetStringOffset(
362 Str: TF->getValueAsString(FieldName: "NegativeDirectiveName"));
363 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {{ \"{0}\" }, {1} },\n", Vals&: ProcName,
364 Vals&: PosDirectiveIdx);
365 OS.indent(NumSpaces: 4) << formatv(Fmt: "{{ {{ \"{0}\" }, {1} },\n", Vals&: ProcName,
366 Vals&: NegDirectiveIdx);
367 }
368 }
369
370 OS << "};\n";
371}
372
373static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
374 emitRISCVExtensions(Records: RK, OS);
375 emitRISCVProfiles(Records: RK, OS);
376 emitRISCVProcs(RK, OS);
377 emitRISCVExtensionBitmask(RK, OS);
378
379 StringToOffsetTable TuneFeatureStrTable;
380 emitRISCVTuneFeatures(RK, StrTable&: TuneFeatureStrTable, OS);
381 emitRISCVConfigurableTuneFeatures(RK, StrTable: TuneFeatureStrTable, OS);
382}
383
384static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef,
385 "Generate the list of CPUs and extensions for "
386 "RISC-V");
387