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 ∈ 212 }; 213 214 template <typename T> struct IfLt { 215 StringRef Op; 216 StringRef Args; 217 const T ∈ 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