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