1//===- RuntimeLibcallEmitter.cpp - Properties from RuntimeLibcalls.td -----===//
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#include "llvm/ADT/StringRef.h"
10#include "llvm/Support/Debug.h"
11#include "llvm/Support/raw_ostream.h"
12#include "llvm/TableGen/Error.h"
13#include "llvm/TableGen/Record.h"
14#include "llvm/TableGen/TableGenBackend.h"
15using namespace llvm;
16
17namespace {
18
19class RuntimeLibcall {
20 const Record *TheDef = nullptr;
21
22public:
23 RuntimeLibcall() = delete;
24 RuntimeLibcall(const Record *Def) : TheDef(Def) { assert(Def); }
25
26 ~RuntimeLibcall() { assert(TheDef); }
27
28 const Record *getDef() const { return TheDef; }
29
30 StringRef getName() const { return TheDef->getName(); }
31
32 void emitEnumEntry(raw_ostream &OS) const {
33 OS << "RTLIB::" << TheDef->getValueAsString(FieldName: "Name");
34 }
35};
36
37class RuntimeLibcallImpl {
38 const Record *TheDef;
39 const RuntimeLibcall *Provides = nullptr;
40
41public:
42 RuntimeLibcallImpl(
43 const Record *Def,
44 const DenseMap<const Record *, const RuntimeLibcall *> &ProvideMap)
45 : TheDef(Def) {
46 if (const Record *ProvidesDef = Def->getValueAsDef(FieldName: "Provides"))
47 Provides = ProvideMap.lookup(Val: ProvidesDef);
48 }
49
50 ~RuntimeLibcallImpl() {}
51
52 const Record *getDef() const { return TheDef; }
53
54 StringRef getName() const { return TheDef->getName(); }
55
56 const RuntimeLibcall *getProvides() const { return Provides; }
57
58 StringRef getLibcallFuncName() const {
59 return TheDef->getValueAsString(FieldName: "LibCallFuncName");
60 }
61
62 void emitQuotedLibcallFuncName(raw_ostream &OS) const {
63 OS << '\"' << getLibcallFuncName() << '\"';
64 }
65
66 bool isDefault() const { return TheDef->getValueAsBit(FieldName: "IsDefault"); }
67
68 void emitEnumEntry(raw_ostream &OS) const {
69 OS << "RTLIB::" << TheDef->getName();
70 }
71};
72
73class RuntimeLibcallEmitter {
74private:
75 const RecordKeeper &Records;
76
77 DenseMap<const Record *, const RuntimeLibcall *> Def2RuntimeLibcall;
78
79 const RuntimeLibcall *getRuntimeLibcall(const Record *Def) const {
80 return Def2RuntimeLibcall.lookup(Val: Def);
81 }
82
83 std::vector<RuntimeLibcall> RuntimeLibcallDefList;
84 std::vector<RuntimeLibcallImpl> RuntimeLibcallImplDefList;
85
86 DenseMap<const RuntimeLibcall *, const RuntimeLibcallImpl *>
87 LibCallToDefaultImpl;
88
89 void
90 emitTargetOverrideFunc(raw_ostream &OS, StringRef FuncName,
91 ArrayRef<RuntimeLibcallImpl> LibCallImplList) const;
92
93 void emitGetRuntimeLibcallEnum(raw_ostream &OS) const;
94
95 void emitWindowsArm64LibCallNameOverrides(raw_ostream &OS) const;
96
97 void emitGetInitRuntimeLibcallNames(raw_ostream &OS) const;
98 void emitGetInitRuntimeLibcallUtils(raw_ostream &OS) const;
99
100public:
101 RuntimeLibcallEmitter(const RecordKeeper &R) : Records(R) {
102
103 ArrayRef<const Record *> AllRuntimeLibcalls =
104 Records.getAllDerivedDefinitions(ClassName: "RuntimeLibcall");
105
106 RuntimeLibcallDefList.reserve(n: AllRuntimeLibcalls.size());
107
108 for (const Record *RuntimeLibcallDef : AllRuntimeLibcalls) {
109 RuntimeLibcallDefList.emplace_back(args&: RuntimeLibcallDef);
110 Def2RuntimeLibcall[RuntimeLibcallDef] = &RuntimeLibcallDefList.back();
111 }
112
113 for (RuntimeLibcall &LibCall : RuntimeLibcallDefList)
114 Def2RuntimeLibcall[LibCall.getDef()] = &LibCall;
115
116 ArrayRef<const Record *> AllRuntimeLibcallImpls =
117 Records.getAllDerivedDefinitions(ClassName: "RuntimeLibcallImpl");
118 RuntimeLibcallImplDefList.reserve(n: AllRuntimeLibcallImpls.size());
119
120 for (const Record *LibCallImplDef : AllRuntimeLibcallImpls) {
121 RuntimeLibcallImplDefList.emplace_back(args&: LibCallImplDef,
122 args&: Def2RuntimeLibcall);
123
124 RuntimeLibcallImpl &LibCallImpl = RuntimeLibcallImplDefList.back();
125
126 // const RuntimeLibcallImpl &LibCallImpl =
127 // RuntimeLibcallImplDefList.back();
128 if (LibCallImpl.isDefault()) {
129 const RuntimeLibcall *Provides = LibCallImpl.getProvides();
130 if (!Provides)
131 PrintFatalError(ErrorLoc: LibCallImplDef->getLoc(),
132 Msg: "default implementations must provide a libcall");
133 LibCallToDefaultImpl[Provides] = &LibCallImpl;
134 }
135 }
136 }
137
138 std::vector<RuntimeLibcallImpl>
139 getRuntimeLibcallImplSet(StringRef Name) const {
140 std::vector<RuntimeLibcallImpl> Result;
141 ArrayRef<const Record *> ImplSet =
142 Records.getAllDerivedDefinitionsIfDefined(ClassName: Name);
143 Result.reserve(n: ImplSet.size());
144
145 for (const Record *LibCallImplDef : ImplSet)
146 Result.emplace_back(args&: LibCallImplDef, args: Def2RuntimeLibcall);
147 return Result;
148 }
149
150 void run(raw_ostream &OS);
151};
152
153} // End anonymous namespace.
154
155/// Emit a method \p FuncName of RTLIB::RuntimeLibcallsInfo to override the
156/// libcall names in \p LibCallImplList.
157void RuntimeLibcallEmitter::emitTargetOverrideFunc(
158 raw_ostream &OS, StringRef FuncName,
159 ArrayRef<RuntimeLibcallImpl> LibCallImplList) const {
160 OS << "void llvm::RTLIB::RuntimeLibcallsInfo::" << FuncName << "() {\n";
161
162 if (LibCallImplList.empty()) {
163 OS << " llvm_unreachable(\"override set not defined\");\n";
164 } else {
165 // for (const Record *LibCallImpl : LibCallImplList) {
166 for (const RuntimeLibcallImpl &LibCallImpl : LibCallImplList) {
167 const RuntimeLibcall *Provides = LibCallImpl.getProvides();
168 OS << " LibcallImpls[";
169 Provides->emitEnumEntry(OS);
170 OS << "] = ";
171 LibCallImpl.emitEnumEntry(OS);
172 OS << ";\n";
173 }
174 }
175
176 OS << "}\n\n";
177}
178
179void RuntimeLibcallEmitter::emitGetRuntimeLibcallEnum(raw_ostream &OS) const {
180 OS << "#ifdef GET_RUNTIME_LIBCALL_ENUM\n"
181 "namespace llvm {\n"
182 "namespace RTLIB {\n"
183 "enum Libcall : unsigned short {\n";
184
185 size_t CallTypeEnumVal = 0;
186 for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {
187 StringRef Name = LibCall.getName();
188 OS << " " << Name << " = " << CallTypeEnumVal++ << ",\n";
189 }
190
191 // TODO: Emit libcall names as string offset table.
192
193 OS << " UNKNOWN_LIBCALL = " << CallTypeEnumVal
194 << "\n};\n\n"
195 "enum LibcallImpl : unsigned short {\n"
196 " Unsupported = 0,\n";
197
198 // FIXME: Emit this in a different namespace. And maybe use enum class.
199 size_t LibCallImplEnumVal = 1;
200 for (const RuntimeLibcallImpl &LibCall : RuntimeLibcallImplDefList) {
201 OS << " " << LibCall.getName() << " = " << LibCallImplEnumVal++ << ", // "
202 << LibCall.getLibcallFuncName() << '\n';
203 }
204
205 OS << " NumLibcallImpls = " << LibCallImplEnumVal
206 << "\n};\n"
207 "} // End namespace RTLIB\n"
208 "} // End namespace llvm\n"
209 "#endif\n\n";
210}
211
212void RuntimeLibcallEmitter::emitWindowsArm64LibCallNameOverrides(
213 raw_ostream &OS) const {
214 // FIXME: Stop treating this as a special case
215 OS << "void "
216 "llvm::RTLIB::RuntimeLibcallsInfo::setWindowsArm64LibCallNameOverrides("
217 ") {\n"
218 " static const RTLIB::LibcallImpl "
219 "WindowsArm64RoutineImpls[RTLIB::UNKNOWN_LIBCALL + 1] = {\n";
220 for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {
221 auto I = LibCallToDefaultImpl.find(Val: &LibCall);
222 if (I == LibCallToDefaultImpl.end())
223 OS << " RTLIB::Unsupported,";
224 else {
225 const RuntimeLibcallImpl *LibCallImpl = I->second;
226 assert(LibCallImpl);
227 OS << " RTLIB::arm64ec_" << LibCallImpl->getName() << ',';
228 }
229
230 OS << " // ";
231 LibCall.emitEnumEntry(OS);
232 OS << '\n';
233 }
234
235 OS << " RTLIB::Unsupported // RTLIB::UNKNOWN_LIBCALL\n"
236 " };\n\n"
237 " std::memcpy(LibcallImpls, WindowsArm64RoutineImpls,\n"
238 " sizeof(LibcallImpls));\n"
239 " static_assert(sizeof(LibcallImpls) == "
240 "sizeof(WindowsArm64RoutineImpls),\n"
241 " \"libcall array size should match\");\n"
242 "}\n#endif\n\n";
243}
244
245void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallNames(
246 raw_ostream &OS) const {
247 // TODO: Emit libcall names as string offset table.
248
249 OS << "#ifdef GET_INIT_RUNTIME_LIBCALL_NAMES\n"
250 "const RTLIB::LibcallImpl "
251 "llvm::RTLIB::RuntimeLibcallsInfo::"
252 "DefaultLibcallImpls[RTLIB::UNKNOWN_LIBCALL + 1] = {\n";
253
254 for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {
255 auto I = LibCallToDefaultImpl.find(Val: &LibCall);
256 if (I == LibCallToDefaultImpl.end()) {
257 OS << " RTLIB::Unsupported,";
258 } else {
259 const RuntimeLibcallImpl *LibCallImpl = I->second;
260 OS << " ";
261 LibCallImpl->emitEnumEntry(OS);
262 OS << ",";
263 }
264
265 OS << " // ";
266 LibCall.emitEnumEntry(OS);
267 OS << '\n';
268 }
269
270 OS << " RTLIB::Unsupported\n"
271 "};\n\n";
272
273 // Emit the implementation names
274 OS << "const char *const llvm::RTLIB::RuntimeLibcallsInfo::"
275 "LibCallImplNames[RTLIB::NumLibcallImpls] = {\n"
276 " nullptr, // RTLIB::Unsupported\n";
277
278 for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
279 OS << " \"" << LibCallImpl.getLibcallFuncName() << "\", // ";
280 LibCallImpl.emitEnumEntry(OS);
281 OS << '\n';
282 }
283
284 OS << "};\n\n";
285
286 // Emit the reverse mapping from implementation libraries to RTLIB::Libcall
287 OS << "const RTLIB::Libcall llvm::RTLIB::RuntimeLibcallsInfo::"
288 "ImplToLibcall[RTLIB::NumLibcallImpls] = {\n"
289 " RTLIB::UNKNOWN_LIBCALL, // RTLIB::Unsupported\n";
290
291 for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
292 const RuntimeLibcall *Provides = LibCallImpl.getProvides();
293 OS << " ";
294 Provides->emitEnumEntry(OS);
295 OS << ", // ";
296 LibCallImpl.emitEnumEntry(OS);
297 OS << '\n';
298 }
299 OS << "};\n\n";
300
301 std::vector<RuntimeLibcallImpl> ZOSRuntimeLibcallImplList =
302 getRuntimeLibcallImplSet(Name: "ZOSRuntimeLibcallImpl");
303 emitTargetOverrideFunc(OS, FuncName: "setZOSLibCallNameOverrides",
304 LibCallImplList: ZOSRuntimeLibcallImplList);
305
306 std::vector<RuntimeLibcallImpl> PPCRuntimeLibcallImplList =
307 getRuntimeLibcallImplSet(Name: "PPCRuntimeLibcallImpl");
308 emitTargetOverrideFunc(OS, FuncName: "setPPCLibCallNameOverrides",
309 LibCallImplList: PPCRuntimeLibcallImplList);
310
311 emitWindowsArm64LibCallNameOverrides(OS);
312}
313
314void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallUtils(
315 raw_ostream &OS) const {
316 // FIXME: Hack we shouldn't really need
317 OS << "#ifdef GET_INIT_RUNTIME_LIBCALL_UTILS\n"
318 "static inline bool isAtomicLibCall(llvm::RTLIB::Libcall LC) {\n"
319 " switch (LC) {\n";
320 for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {
321 StringRef Name = LibCall.getName();
322 if (Name.contains(Other: "ATOMIC")) {
323 OS << " case ";
324 LibCall.emitEnumEntry(OS);
325 OS << ":\n";
326 }
327 }
328
329 OS << " return true;\n"
330 " default:\n"
331 " return false;\n"
332 " }\n\n"
333 " llvm_unreachable(\"covered switch over libcalls\");\n"
334 "}\n#endif\n\n";
335}
336
337void RuntimeLibcallEmitter::run(raw_ostream &OS) {
338 emitSourceFileHeader(Desc: "Runtime LibCalls Source Fragment", OS, Record: Records);
339 emitGetRuntimeLibcallEnum(OS);
340 emitGetInitRuntimeLibcallNames(OS);
341 emitGetInitRuntimeLibcallUtils(OS);
342}
343
344static TableGen::Emitter::OptClass<RuntimeLibcallEmitter>
345 X("gen-runtime-libcalls", "Generate RuntimeLibcalls");
346