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.
getCtx(FunctionType FT)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
getAligntoString(const Context & Ctx,const AlignArg & AlignTo)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
operator <<(raw_ostream & Stream,const ElementType & E)188 static raw_ostream &operator<<(raw_ostream &Stream, const ElementType &E) {
189 return Stream << '_' << E.Size;
190 }
operator <<(raw_ostream & Stream,const Individual & O)191 static raw_ostream &operator<<(raw_ostream &Stream, const Individual &O) {
192 return Stream << O.Element;
193 }
operator <<(raw_ostream & Stream,const Overlap & O)194 static raw_ostream &operator<<(raw_ostream &Stream, const Overlap &O) {
195 return Stream << "HeadTail<" << O.Element << '>';
196 }
operator <<(raw_ostream & Stream,const Loop & O)197 static raw_ostream &operator<<(raw_ostream &Stream, const Loop &O) {
198 return Stream << "Loop<" << O.Element << '>';
199 }
operator <<(raw_ostream & Stream,const AlignedLoop & O)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 }
operator <<(raw_ostream & Stream,const Accelerator & O)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 ∈
212 };
213
214 template <typename T> struct IfLt {
215 StringRef Op;
216 StringRef Args;
217 const T ∈
218 };
219
operator <<(raw_ostream & Stream,const Zero & O)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>
operator <<(raw_ostream & Stream,const IfEq<T> & O)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>
operator <<(raw_ostream & Stream,const IfLt<T> & O)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
operator <<(raw_ostream & Stream,const ElementTypeClass & Class)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
operator <<(raw_ostream & Stream,const FunctionImplementation & FI)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
getImplementation(const NamedFunctionDescriptor & NamedFD)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
Serialize(raw_ostream & Stream,ArrayRef<NamedFunctionDescriptor> Descriptors)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
operator <<(raw_ostream & Stream,const SizeSpan & SS)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 }
operator <<(raw_ostream & Stream,const Contiguous & O)337 static raw_ostream &operator<<(raw_ostream &Stream, const Contiguous &O) {
338 return Stream << "Contiguous{" << O.Span << '}';
339 }
operator <<(raw_ostream & Stream,const Overlap & O)340 static raw_ostream &operator<<(raw_ostream &Stream, const Overlap &O) {
341 return Stream << "Overlap{" << O.Span << '}';
342 }
operator <<(raw_ostream & Stream,const Loop & O)343 static raw_ostream &operator<<(raw_ostream &Stream, const Loop &O) {
344 return Stream << "Loop{" << O.Span << ',' << O.BlockSize << '}';
345 }
operator <<(raw_ostream & Stream,const AlignArg & O)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 }
operator <<(raw_ostream & Stream,const AlignedLoop & O)356 static raw_ostream &operator<<(raw_ostream &Stream, const AlignedLoop &O) {
357 return Stream << "AlignedLoop{" << O.Loop << ',' << O.Alignment << ','
358 << O.AlignTo << '}';
359 }
operator <<(raw_ostream & Stream,const Accelerator & O)360 static raw_ostream &operator<<(raw_ostream &Stream, const Accelerator &O) {
361 return Stream << "Accelerator{" << O.Span << '}';
362 }
operator <<(raw_ostream & Stream,const ElementTypeClass & O)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 }
operator <<(raw_ostream & Stream,const FunctionType & T)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>
operator <<(raw_ostream & Stream,const llvm::Optional<T> & MaybeT)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 }
operator <<(raw_ostream & Stream,const FunctionDescriptor & FD)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 }
operator <<(raw_ostream & Stream,const NamedFunctionDescriptor & NFD)400 static raw_ostream &operator<<(raw_ostream &Stream,
401 const NamedFunctionDescriptor &NFD) {
402 return Stream << '{' << '"' << NFD.Name << '"' << ',' << NFD.Desc << '}';
403 }
404 template <typename T>
operator <<(raw_ostream & Stream,const std::vector<T> & VectorT)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
Serialize(raw_ostream & Stream,ArrayRef<NamedFunctionDescriptor> Descriptors)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
operator <<(raw_ostream & Stream,const FunctionName & FN)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
operator <<(raw_ostream & Stream,const ReturnType & RT)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
operator <<(raw_ostream & Stream,const NamedFunctionDescriptor * FD)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 &
operator <<(raw_ostream & Stream,const std::vector<const NamedFunctionDescriptor * > & Descriptors)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
operator <<(raw_ostream & Stream,const Configuration & C)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
Serialize(raw_ostream & Stream,FunctionType FT,ArrayRef<NamedFunctionDescriptor> Descriptors)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
Serialize(raw_ostream & Stream,ArrayRef<NamedFunctionDescriptor> Descriptors)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.
getInternalizedString(std::string VolatileStr)615 StringRef getInternalizedString(std::string VolatileStr) {
616 static llvm::StringSet StringCache;
617 return StringCache.insert(std::move(VolatileStr)).first->getKey();
618 }
619
getString(FunctionType FT)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
Serialize(raw_ostream & Stream,ArrayRef<FunctionDescriptor> Descriptors)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