1//===-- C++ code generation from NamedFunctionDescriptors -----------------===//
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// This code is responsible for generating the "Implementation.cpp" file.
9// The file is composed like this:
10//
11// 1. Includes
12// 2. Using statements to help readability.
13// 3. Source code for all the mem function implementations.
14// 4. The function to retrieve all the function descriptors with their name.
15// llvm::ArrayRef<NamedFunctionDescriptor> getFunctionDescriptors();
16// 5. The functions for the benchmarking infrastructure:
17// llvm::ArrayRef<MemcpyConfiguration> getMemcpyConfigurations();
18// llvm::ArrayRef<MemcmpOrBcmpConfiguration> getMemcmpConfigurations();
19// llvm::ArrayRef<MemcmpOrBcmpConfiguration> getBcmpConfigurations();
20// llvm::ArrayRef<MemsetConfiguration> getMemsetConfigurations();
21// llvm::ArrayRef<BzeroConfiguration> getBzeroConfigurations();
22//
23//
24// Sections 3, 4 and 5 are handled by the following namespaces:
25// - codegen::functions
26// - codegen::descriptors
27// - codegen::configurations
28//
29// The programming style is functionnal. In each of these namespace, the
30// original `NamedFunctionDescriptor` object is turned into a different type. We
31// make use of overloaded stream operators to format the resulting type into
32// either a function, a descriptor or a configuration. The entry point of each
33// namespace is the Serialize function.
34//
35// Note the code here is better understood by starting from the `Serialize`
36// function at the end of the file.
37
38#include "automemcpy/CodeGen.h"
39#include <cassert>
40#include <llvm/ADT/STLExtras.h>
41#include <llvm/ADT/StringSet.h>
42#include <llvm/Support/FormatVariadic.h>
43#include <llvm/Support/raw_ostream.h>
44#include <optional>
45#include <set>
46
47namespace llvm {
48namespace automemcpy {
49namespace codegen {
50
51// The indentation string.
52static constexpr StringRef kIndent = " ";
53
54// The codegen namespace handles the serialization of a NamedFunctionDescriptor
55// into source code for the function, the descriptor and the configuration.
56
57namespace functions {
58
59// This namespace turns a NamedFunctionDescriptor into an actual implementation.
60// -----------------------------------------------------------------------------
61// e.g.
62// static void memcpy_0xB20D4702493C397E(char *__restrict dst,
63// const char *__restrict src,
64// size_t size) {
65// using namespace LIBC_NAMESPACE::x86;
66// if(size == 0) return;
67// if(size == 1) return copy<_1>(dst, src);
68// if(size < 4) return copy<HeadTail<_2>>(dst, src, size);
69// if(size < 8) return copy<HeadTail<_4>>(dst, src, size);
70// if(size < 16) return copy<HeadTail<_8>>(dst, src, size);
71// if(size < 32) return copy<HeadTail<_16>>(dst, src, size);
72// return copy<Accelerator>(dst, src, size);
73// }
74
75// The `Serialize` method turns a `NamedFunctionDescriptor` into a
76// `FunctionImplementation` which holds all the information needed to produce
77// the C++ source code.
78
79// An Element with its size (e.g. `_16` in the example above).
80struct ElementType {
81 size_t Size;
82};
83// The case `if(size == 0)` is encoded as a the Zero type.
84struct Zero {
85 StringRef DefaultReturnValue;
86};
87// An individual size `if(size == X)` is encoded as an Individual type.
88struct Individual {
89 size_t IfEq;
90 ElementType Element;
91};
92// An overlap strategy is encoded as an Overlap type.
93struct Overlap {
94 size_t IfLt;
95 ElementType Element;
96};
97// A loop strategy is encoded as a Loop type.
98struct Loop {
99 size_t IfLt;
100 ElementType Element;
101};
102// An aligned loop strategy is encoded as an AlignedLoop type.
103struct AlignedLoop {
104 size_t IfLt;
105 ElementType Element;
106 ElementType Alignment;
107 StringRef AlignTo;
108};
109// The accelerator strategy.
110struct Accelerator {
111 size_t IfLt;
112};
113// The Context stores data about the function type.
114struct Context {
115 StringRef FunctionReturnType; // e.g. void* or int
116 StringRef FunctionArgs;
117 StringRef ElementOp; // copy, three_way_compare, splat_set, ...
118 StringRef FixedSizeArgs;
119 StringRef RuntimeSizeArgs;
120 StringRef DefaultReturnValue;
121};
122// A detailed representation of the function implementation mapped from the
123// NamedFunctionDescriptor.
124struct FunctionImplementation {
125 Context Ctx;
126 StringRef Name;
127 std::vector<Individual> Individuals;
128 std::vector<Overlap> Overlaps;
129 std::optional<Loop> Loop;
130 std::optional<AlignedLoop> AlignedLoop;
131 std::optional<Accelerator> Accelerator;
132 ElementTypeClass ElementClass;
133};
134
135// Returns the Context for each FunctionType.
136static Context getCtx(FunctionType FT) {
137 switch (FT) {
138 case FunctionType::MEMCPY:
139 return {.FunctionReturnType: "void",
140 .FunctionArgs: "(char *__restrict dst, const char *__restrict src, size_t size)",
141 .ElementOp: "copy",
142 .FixedSizeArgs: "(dst, src)",
143 .RuntimeSizeArgs: "(dst, src, size)",
144 .DefaultReturnValue: ""};
145 case FunctionType::MEMCMP:
146 return {.FunctionReturnType: "int",
147 .FunctionArgs: "(const char * lhs, const char * rhs, size_t size)",
148 .ElementOp: "three_way_compare",
149 .FixedSizeArgs: "(lhs, rhs)",
150 .RuntimeSizeArgs: "(lhs, rhs, size)",
151 .DefaultReturnValue: "0"};
152 case FunctionType::MEMSET:
153 return {.FunctionReturnType: "void",
154 .FunctionArgs: "(char * dst, int value, size_t size)",
155 .ElementOp: "splat_set",
156 .FixedSizeArgs: "(dst, value)",
157 .RuntimeSizeArgs: "(dst, value, size)",
158 .DefaultReturnValue: ""};
159 case FunctionType::BZERO:
160 return {.FunctionReturnType: "void", .FunctionArgs: "(char * dst, size_t size)",
161 .ElementOp: "splat_set", .FixedSizeArgs: "(dst, 0)",
162 .RuntimeSizeArgs: "(dst, 0, size)", .DefaultReturnValue: ""};
163 default:
164 report_fatal_error(reason: "Not yet implemented");
165 }
166}
167
168static StringRef getAligntoString(const AlignArg &AlignTo) {
169 switch (AlignTo) {
170 case AlignArg::_1:
171 return "Arg::P1";
172 case AlignArg::_2:
173 return "Arg::P2";
174 case AlignArg::ARRAY_SIZE:
175 report_fatal_error(reason: "logic error");
176 }
177}
178
179static raw_ostream &operator<<(raw_ostream &Stream, const ElementType &E) {
180 return Stream << '_' << E.Size;
181}
182static raw_ostream &operator<<(raw_ostream &Stream, const Individual &O) {
183 return Stream << O.Element;
184}
185static raw_ostream &operator<<(raw_ostream &Stream, const Overlap &O) {
186 return Stream << "HeadTail<" << O.Element << '>';
187}
188static raw_ostream &operator<<(raw_ostream &Stream, const Loop &O) {
189 return Stream << "Loop<" << O.Element << '>';
190}
191static raw_ostream &operator<<(raw_ostream &Stream, const AlignedLoop &O) {
192 return Stream << "Align<" << O.Alignment << ',' << O.AlignTo << ">::Then<"
193 << Loop{.IfLt: O.IfLt, .Element: O.Element} << ">";
194}
195static raw_ostream &operator<<(raw_ostream &Stream, const Accelerator &O) {
196 return Stream << "Accelerator";
197}
198
199template <typename T> struct IfEq {
200 StringRef Op;
201 StringRef Args;
202 const T &Element;
203};
204
205template <typename T> struct IfLt {
206 StringRef Op;
207 StringRef Args;
208 const T &Element;
209};
210
211static raw_ostream &operator<<(raw_ostream &Stream, const Zero &O) {
212 Stream << kIndent << "if(size == 0) return";
213 if (!O.DefaultReturnValue.empty())
214 Stream << ' ' << O.DefaultReturnValue;
215 return Stream << ";\n";
216}
217
218template <typename T>
219static raw_ostream &operator<<(raw_ostream &Stream, const IfEq<T> &O) {
220 return Stream << kIndent << "if(size == " << O.Element.IfEq << ") return "
221 << O.Op << '<' << O.Element << '>' << O.Args << ";\n";
222}
223
224template <typename T>
225static raw_ostream &operator<<(raw_ostream &Stream, const IfLt<T> &O) {
226 Stream << kIndent;
227 if (O.Element.IfLt != kMaxSize)
228 Stream << "if(size < " << O.Element.IfLt << ") ";
229 return Stream << "return " << O.Op << '<' << O.Element << '>' << O.Args
230 << ";\n";
231}
232
233static raw_ostream &operator<<(raw_ostream &Stream,
234 const ElementTypeClass &Class) {
235 switch (Class) {
236 case ElementTypeClass::SCALAR:
237 return Stream << "scalar";
238 case ElementTypeClass::BUILTIN:
239 return Stream << "builtin";
240 case ElementTypeClass::NATIVE:
241 // FIXME: the framework should provide a `native` namespace that redirect to
242 // x86, arm or other architectures.
243 return Stream << "x86";
244 }
245}
246
247static raw_ostream &operator<<(raw_ostream &Stream,
248 const FunctionImplementation &FI) {
249 const auto &Ctx = FI.Ctx;
250 Stream << "static " << Ctx.FunctionReturnType << ' ' << FI.Name
251 << Ctx.FunctionArgs << " {\n";
252 Stream << kIndent << "using namespace LIBC_NAMESPACE::" << FI.ElementClass
253 << ";\n";
254 for (const auto &I : FI.Individuals)
255 if (I.Element.Size == 0)
256 Stream << Zero{.DefaultReturnValue: Ctx.DefaultReturnValue};
257 else
258 Stream << IfEq<Individual>{.Op: Ctx.ElementOp, .Args: Ctx.FixedSizeArgs, .Element: I};
259 for (const auto &O : FI.Overlaps)
260 Stream << IfLt<Overlap>{.Op: Ctx.ElementOp, .Args: Ctx.RuntimeSizeArgs, .Element: O};
261 if (const auto &C = FI.Loop)
262 Stream << IfLt<Loop>{.Op: Ctx.ElementOp, .Args: Ctx.RuntimeSizeArgs, .Element: *C};
263 if (const auto &C = FI.AlignedLoop)
264 Stream << IfLt<AlignedLoop>{.Op: Ctx.ElementOp, .Args: Ctx.RuntimeSizeArgs, .Element: *C};
265 if (const auto &C = FI.Accelerator)
266 Stream << IfLt<Accelerator>{.Op: Ctx.ElementOp, .Args: Ctx.RuntimeSizeArgs, .Element: *C};
267 return Stream << "}\n";
268}
269
270// Turns a `NamedFunctionDescriptor` into a `FunctionImplementation` unfolding
271// the contiguous and overlap region into several statements. The zero case is
272// also mapped to its own type.
273static FunctionImplementation
274getImplementation(const NamedFunctionDescriptor &NamedFD) {
275 const FunctionDescriptor &FD = NamedFD.Desc;
276 FunctionImplementation Impl;
277 Impl.Ctx = getCtx(FD.Type);
278 Impl.Name = NamedFD.Name;
279 Impl.ElementClass = FD.ElementClass;
280 if (auto C = FD.Contiguous)
281 for (size_t I = C->Span.Begin; I < C->Span.End; ++I)
282 Impl.Individuals.push_back(x: Individual{.IfEq: I, .Element: ElementType{.Size: I}});
283 if (auto C = FD.Overlap)
284 for (size_t I = C->Span.Begin; I < C->Span.End; I *= 2)
285 Impl.Overlaps.push_back(x: Overlap{.IfLt: 2 * I, .Element: ElementType{.Size: I}});
286 if (const auto &L = FD.Loop)
287 Impl.Loop = Loop{L->Span.End, ElementType{L->BlockSize}};
288 if (const auto &AL = FD.AlignedLoop)
289 Impl.AlignedLoop =
290 AlignedLoop{AL->Loop.Span.End, ElementType{AL->Loop.BlockSize},
291 ElementType{AL->Alignment}, getAligntoString(AL->AlignTo)};
292 if (const auto &A = FD.Accelerator)
293 Impl.Accelerator = Accelerator{A->Span.End};
294 return Impl;
295}
296
297static void Serialize(raw_ostream &Stream,
298 ArrayRef<NamedFunctionDescriptor> Descriptors) {
299
300 for (const auto &FD : Descriptors)
301 Stream << getImplementation(FD);
302}
303
304} // namespace functions
305
306namespace descriptors {
307
308// This namespace generates the getFunctionDescriptors function:
309// -------------------------------------------------------------
310// e.g.
311// ArrayRef<NamedFunctionDescriptor> getFunctionDescriptors() {
312// static constexpr NamedFunctionDescriptor kDescriptors[] = {
313// {"memcpy_0xE00E29EE73994E2B",{FunctionType::MEMCPY,std::nullopt,std::nullopt,std::nullopt,std::nullopt,Accelerator{{0,kMaxSize}},ElementTypeClass::NATIVE}},
314// {"memcpy_0x8661D80472487AB5",{FunctionType::MEMCPY,Contiguous{{0,1}},std::nullopt,std::nullopt,std::nullopt,Accelerator{{1,kMaxSize}},ElementTypeClass::NATIVE}},
315// ...
316// };
317// return ArrayRef(kDescriptors);
318// }
319
320static raw_ostream &operator<<(raw_ostream &Stream, const SizeSpan &SS) {
321 Stream << "{" << SS.Begin << ',';
322 if (SS.End == kMaxSize)
323 Stream << "kMaxSize";
324 else
325 Stream << SS.End;
326 return Stream << '}';
327}
328static raw_ostream &operator<<(raw_ostream &Stream, const Contiguous &O) {
329 return Stream << "Contiguous{" << O.Span << '}';
330}
331static raw_ostream &operator<<(raw_ostream &Stream, const Overlap &O) {
332 return Stream << "Overlap{" << O.Span << '}';
333}
334static raw_ostream &operator<<(raw_ostream &Stream, const Loop &O) {
335 return Stream << "Loop{" << O.Span << ',' << O.BlockSize << '}';
336}
337static raw_ostream &operator<<(raw_ostream &Stream, const AlignArg &O) {
338 switch (O) {
339 case AlignArg::_1:
340 return Stream << "AlignArg::_1";
341 case AlignArg::_2:
342 return Stream << "AlignArg::_2";
343 case AlignArg::ARRAY_SIZE:
344 report_fatal_error(reason: "logic error");
345 }
346}
347static raw_ostream &operator<<(raw_ostream &Stream, const AlignedLoop &O) {
348 return Stream << "AlignedLoop{" << O.Loop << ',' << O.Alignment << ','
349 << O.AlignTo << '}';
350}
351static raw_ostream &operator<<(raw_ostream &Stream, const Accelerator &O) {
352 return Stream << "Accelerator{" << O.Span << '}';
353}
354static raw_ostream &operator<<(raw_ostream &Stream, const ElementTypeClass &O) {
355 switch (O) {
356 case ElementTypeClass::SCALAR:
357 return Stream << "ElementTypeClass::SCALAR";
358 case ElementTypeClass::BUILTIN:
359 return Stream << "ElementTypeClass::BUILTIN";
360 case ElementTypeClass::NATIVE:
361 return Stream << "ElementTypeClass::NATIVE";
362 }
363}
364static raw_ostream &operator<<(raw_ostream &Stream, const FunctionType &T) {
365 switch (T) {
366 case FunctionType::MEMCPY:
367 return Stream << "FunctionType::MEMCPY";
368 case FunctionType::MEMCMP:
369 return Stream << "FunctionType::MEMCMP";
370 case FunctionType::BCMP:
371 return Stream << "FunctionType::BCMP";
372 case FunctionType::MEMSET:
373 return Stream << "FunctionType::MEMSET";
374 case FunctionType::BZERO:
375 return Stream << "FunctionType::BZERO";
376 }
377}
378template <typename T>
379static raw_ostream &operator<<(raw_ostream &Stream,
380 const std::optional<T> &MaybeT) {
381 if (MaybeT)
382 return Stream << *MaybeT;
383 return Stream << "std::nullopt";
384}
385static raw_ostream &operator<<(raw_ostream &Stream,
386 const FunctionDescriptor &FD) {
387 return Stream << '{' << FD.Type << ',' << FD.Contiguous << ',' << FD.Overlap
388 << ',' << FD.Loop << ',' << FD.AlignedLoop << ','
389 << FD.Accelerator << ',' << FD.ElementClass << '}';
390}
391static raw_ostream &operator<<(raw_ostream &Stream,
392 const NamedFunctionDescriptor &NFD) {
393 return Stream << '{' << '"' << NFD.Name << '"' << ',' << NFD.Desc << '}';
394}
395template <typename T>
396static raw_ostream &operator<<(raw_ostream &Stream,
397 const std::vector<T> &VectorT) {
398 Stream << '{';
399 bool First = true;
400 for (const auto &Obj : VectorT) {
401 if (!First)
402 Stream << ',';
403 Stream << Obj;
404 First = false;
405 }
406 return Stream << '}';
407}
408
409static void Serialize(raw_ostream &Stream,
410 ArrayRef<NamedFunctionDescriptor> Descriptors) {
411 Stream << R"(ArrayRef<NamedFunctionDescriptor> getFunctionDescriptors() {
412 static constexpr NamedFunctionDescriptor kDescriptors[] = {
413)";
414 for (size_t I = 0, E = Descriptors.size(); I < E; ++I) {
415 Stream << kIndent << kIndent << Descriptors[I] << ",\n";
416 }
417 Stream << R"( };
418 return ArrayRef(kDescriptors);
419}
420)";
421}
422
423} // namespace descriptors
424
425namespace configurations {
426
427// This namespace generates the getXXXConfigurations functions:
428// ------------------------------------------------------------
429// e.g.
430// llvm::ArrayRef<MemcpyConfiguration> getMemcpyConfigurations() {
431// using namespace LIBC_NAMESPACE;
432// static constexpr MemcpyConfiguration kConfigurations[] = {
433// {Wrap<memcpy_0xE00E29EE73994E2B>, "memcpy_0xE00E29EE73994E2B"},
434// {Wrap<memcpy_0x8661D80472487AB5>, "memcpy_0x8661D80472487AB5"},
435// ...
436// };
437// return llvm::ArrayRef(kConfigurations);
438// }
439
440// The `Wrap` template function is provided in the `Main` function below.
441// It is used to adapt the gnerated code to the prototype of the C function.
442// For instance, the generated code for a `memcpy` takes `char*` pointers and
443// returns nothing but the original C `memcpy` function take and returns `void*`
444// pointers.
445
446struct FunctionName {
447 FunctionType ForType;
448};
449
450struct ReturnType {
451 FunctionType ForType;
452};
453
454struct Configuration {
455 FunctionName Name;
456 ReturnType Type;
457 std::vector<const NamedFunctionDescriptor *> Descriptors;
458};
459
460static raw_ostream &operator<<(raw_ostream &Stream, const FunctionName &FN) {
461 switch (FN.ForType) {
462 case FunctionType::MEMCPY:
463 return Stream << "getMemcpyConfigurations";
464 case FunctionType::MEMCMP:
465 return Stream << "getMemcmpConfigurations";
466 case FunctionType::BCMP:
467 return Stream << "getBcmpConfigurations";
468 case FunctionType::MEMSET:
469 return Stream << "getMemsetConfigurations";
470 case FunctionType::BZERO:
471 return Stream << "getBzeroConfigurations";
472 }
473}
474
475static raw_ostream &operator<<(raw_ostream &Stream, const ReturnType &RT) {
476 switch (RT.ForType) {
477 case FunctionType::MEMCPY:
478 return Stream << "MemcpyConfiguration";
479 case FunctionType::MEMCMP:
480 case FunctionType::BCMP:
481 return Stream << "MemcmpOrBcmpConfiguration";
482 case FunctionType::MEMSET:
483 return Stream << "MemsetConfiguration";
484 case FunctionType::BZERO:
485 return Stream << "BzeroConfiguration";
486 }
487}
488
489static raw_ostream &operator<<(raw_ostream &Stream,
490 const NamedFunctionDescriptor *FD) {
491 return Stream << formatv("{Wrap<{0}>, \"{0}\"}", FD->Name);
492}
493
494static raw_ostream &
495operator<<(raw_ostream &Stream,
496 const std::vector<const NamedFunctionDescriptor *> &Descriptors) {
497 for (size_t I = 0, E = Descriptors.size(); I < E; ++I)
498 Stream << kIndent << kIndent << Descriptors[I] << ",\n";
499 return Stream;
500}
501
502static raw_ostream &operator<<(raw_ostream &Stream, const Configuration &C) {
503 Stream << "llvm::ArrayRef<" << C.Type << "> " << C.Name << "() {\n";
504 if (C.Descriptors.empty())
505 Stream << kIndent << "return {};\n";
506 else {
507 Stream << kIndent << "using namespace LIBC_NAMESPACE;\n";
508 Stream << kIndent << "static constexpr " << C.Type
509 << " kConfigurations[] = {\n";
510 Stream << C.Descriptors;
511 Stream << kIndent << "};\n";
512 Stream << kIndent << "return llvm::ArrayRef(kConfigurations);\n";
513 }
514 Stream << "}\n";
515 return Stream;
516}
517
518static void Serialize(raw_ostream &Stream, FunctionType FT,
519 ArrayRef<NamedFunctionDescriptor> Descriptors) {
520 Configuration Conf;
521 Conf.Name = {FT};
522 Conf.Type = {FT};
523 for (const auto &FD : Descriptors)
524 if (FD.Desc.Type == FT)
525 Conf.Descriptors.push_back(&FD);
526 Stream << Conf;
527}
528
529} // namespace configurations
530static void Serialize(raw_ostream &Stream,
531 ArrayRef<NamedFunctionDescriptor> Descriptors) {
532 Stream << "// This file is auto-generated by libc/benchmarks/automemcpy.\n";
533 Stream << "// Functions : " << Descriptors.size() << "\n";
534 Stream << "\n";
535 Stream << "#include \"LibcFunctionPrototypes.h\"\n";
536 Stream << "#include \"automemcpy/FunctionDescriptor.h\"\n";
537 Stream << "#include \"src/string/memory_utils/elements.h\"\n";
538 Stream << "\n";
539 Stream << "using llvm::libc_benchmarks::BzeroConfiguration;\n";
540 Stream << "using llvm::libc_benchmarks::MemcmpOrBcmpConfiguration;\n";
541 Stream << "using llvm::libc_benchmarks::MemcpyConfiguration;\n";
542 Stream << "using llvm::libc_benchmarks::MemmoveConfiguration;\n";
543 Stream << "using llvm::libc_benchmarks::MemsetConfiguration;\n";
544 Stream << "\n";
545 Stream << "namespace LIBC_NAMESPACE {\n";
546 Stream << "\n";
547 codegen::functions::Serialize(Stream, Descriptors);
548 Stream << "\n";
549 Stream << "} // namespace LIBC_NAMESPACE\n";
550 Stream << "\n";
551 Stream << "namespace llvm {\n";
552 Stream << "namespace automemcpy {\n";
553 Stream << "\n";
554 codegen::descriptors::Serialize(Stream, Descriptors);
555 Stream << "\n";
556 Stream << "} // namespace automemcpy\n";
557 Stream << "} // namespace llvm\n";
558 Stream << "\n";
559 Stream << R"(
560using MemcpyStub = void (*)(char *__restrict, const char *__restrict, size_t);
561template <MemcpyStub Foo>
562void *Wrap(void *__restrict dst, const void *__restrict src, size_t size) {
563 Foo(reinterpret_cast<char *__restrict>(dst),
564 reinterpret_cast<const char *__restrict>(src), size);
565 return dst;
566}
567)";
568 codegen::configurations::Serialize(Stream, FunctionType::MEMCPY, Descriptors);
569 Stream << R"(
570using MemcmpStub = int (*)(const char *, const char *, size_t);
571template <MemcmpStub Foo>
572int Wrap(const void *lhs, const void *rhs, size_t size) {
573 return Foo(reinterpret_cast<const char *>(lhs),
574 reinterpret_cast<const char *>(rhs), size);
575}
576)";
577 codegen::configurations::Serialize(Stream, FunctionType::MEMCMP, Descriptors);
578 codegen::configurations::Serialize(Stream, FunctionType::BCMP, Descriptors);
579 Stream << R"(
580using MemsetStub = void (*)(char *, int, size_t);
581template <MemsetStub Foo> void *Wrap(void *dst, int value, size_t size) {
582 Foo(reinterpret_cast<char *>(dst), value, size);
583 return dst;
584}
585)";
586 codegen::configurations::Serialize(Stream, FunctionType::MEMSET, Descriptors);
587 Stream << R"(
588using BzeroStub = void (*)(char *, size_t);
589template <BzeroStub Foo> void Wrap(void *dst, size_t size) {
590 Foo(reinterpret_cast<char *>(dst), size);
591}
592)";
593 codegen::configurations::Serialize(Stream, FunctionType::BZERO, Descriptors);
594 Stream << R"(
595llvm::ArrayRef<MemmoveConfiguration> getMemmoveConfigurations() {
596 return {};
597}
598)";
599 Stream << "// Functions : " << Descriptors.size() << "\n";
600}
601
602} // namespace codegen
603
604// Stores `VolatileStr` into a cache and returns a StringRef of the cached
605// version.
606StringRef getInternalizedString(std::string VolatileStr) {
607 static llvm::StringSet StringCache;
608 return StringCache.insert(key: std::move(VolatileStr)).first->getKey();
609}
610
611static StringRef getString(FunctionType FT) {
612 switch (FT) {
613 case FunctionType::MEMCPY:
614 return "memcpy";
615 case FunctionType::MEMCMP:
616 return "memcmp";
617 case FunctionType::BCMP:
618 return "bcmp";
619 case FunctionType::MEMSET:
620 return "memset";
621 case FunctionType::BZERO:
622 return "bzero";
623 }
624}
625
626void Serialize(raw_ostream &Stream, ArrayRef<FunctionDescriptor> Descriptors) {
627 std::vector<NamedFunctionDescriptor> FunctionDescriptors;
628 FunctionDescriptors.reserve(Descriptors.size());
629 for (auto &FD : Descriptors) {
630 FunctionDescriptors.emplace_back();
631 FunctionDescriptors.back().Name = getInternalizedString(
632 formatv("{0}_{1:X16}", getString(FD.Type), FD.id()));
633 FunctionDescriptors.back().Desc = std::move(FD);
634 }
635 // Sort functions so they are easier to spot in the generated C++ file.
636 std::sort(FunctionDescriptors.begin(), FunctionDescriptors.end(),
637 [](const NamedFunctionDescriptor &A,
638 const NamedFunctionDescriptor &B) { return A.Desc < B.Desc; });
639 codegen::Serialize(Stream, FunctionDescriptors);
640}
641
642} // namespace automemcpy
643} // namespace llvm
644

source code of libc/benchmarks/automemcpy/lib/CodeGen.cpp