1 //===-- Elementary operations to compose memory primitives ----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H 10 #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H 11 12 #include <stddef.h> // size_t 13 #include <stdint.h> // uint8_t, uint16_t, uint32_t, uint64_t 14 15 #include "src/__support/endian.h" 16 #include "src/string/memory_utils/utils.h" 17 18 namespace __llvm_libc { 19 20 // Elementary Operations 21 // -------------------------------- 22 // We define abstract elementary operations acting on fixed chunks of memory. 23 // These are low level building blocks that are meant to be assembled to compose 24 // higher order abstractions. Each function is defined twice: once with 25 // fixed-size operations, and once with runtime-size operations. 26 27 // Fixed-size copy from 'src' to 'dst'. 28 template <typename Element> 29 void copy(char *__restrict dst, const char *__restrict src) { 30 Element::copy(dst, src); 31 } 32 // Runtime-size copy from 'src' to 'dst'. 33 template <typename Element> 34 void copy(char *__restrict dst, const char *__restrict src, size_t size) { 35 Element::copy(dst, src, size); 36 } 37 38 // Fixed-size move from 'src' to 'dst'. 39 template <typename Element> void move(char *dst, const char *src) { 40 Element::move(dst, src); 41 } 42 // Runtime-size move from 'src' to 'dst'. 43 template <typename Element> void move(char *dst, const char *src, size_t size) { 44 Element::move(dst, src, size); 45 } 46 // Runtime-size move from 'src' to 'dst'. 47 template <typename Element> 48 void move_backward(char *dst, const char *src, size_t size) { 49 Element::move_backward(dst, src, size); 50 } 51 52 // Fixed-size equality between 'lhs' and 'rhs'. 53 template <typename Element> bool equals(const char *lhs, const char *rhs) { 54 return Element::equals(lhs, rhs); 55 } 56 // Runtime-size equality between 'lhs' and 'rhs'. 57 template <typename Element> 58 bool equals(const char *lhs, const char *rhs, size_t size) { 59 return Element::equals(lhs, rhs, size); 60 } 61 62 // Fixed-size three-way comparison between 'lhs' and 'rhs'. 63 template <typename Element> 64 int three_way_compare(const char *lhs, const char *rhs) { 65 return Element::three_way_compare(lhs, rhs); 66 } 67 // Runtime-size three-way comparison between 'lhs' and 'rhs'. 68 template <typename Element> 69 int three_way_compare(const char *lhs, const char *rhs, size_t size) { 70 return Element::three_way_compare(lhs, rhs, size); 71 } 72 73 // Fixed-size initialization. 74 template <typename Element> 75 void splat_set(char *dst, const unsigned char value) { 76 Element::splat_set(dst, value); 77 } 78 // Runtime-size initialization. 79 template <typename Element> 80 void splat_set(char *dst, const unsigned char value, size_t size) { 81 Element::splat_set(dst, value, size); 82 } 83 84 // Stack placeholder for Move operations. 85 template <typename Element> struct Storage { char bytes[Element::SIZE]; }; 86 87 // Fixed-size Higher-Order Operations 88 // ---------------------------------- 89 // - Repeated<Type, ElementCount>: Repeat the operation several times in a row. 90 // - Chained<Types...>: Chain the operation of several types. 91 92 // Repeat the operation several times in a row. 93 template <typename Element, size_t ElementCount> struct Repeated { 94 static constexpr size_t SIZE = ElementCount * Element::SIZE; 95 96 static void copy(char *__restrict dst, const char *__restrict src) { 97 for (size_t i = 0; i < ElementCount; ++i) { 98 const size_t offset = i * Element::SIZE; 99 Element::copy(dst + offset, src + offset); 100 } 101 } 102 103 static void move(char *dst, const char *src) { 104 const auto value = load(src); 105 store(dst, value); 106 } 107 108 static bool equals(const char *lhs, const char *rhs) { 109 for (size_t i = 0; i < ElementCount; ++i) { 110 const size_t offset = i * Element::SIZE; 111 if (!Element::equals(lhs + offset, rhs + offset)) 112 return false; 113 } 114 return true; 115 } 116 117 static int three_way_compare(const char *lhs, const char *rhs) { 118 for (size_t i = 0; i < ElementCount; ++i) { 119 const size_t offset = i * Element::SIZE; 120 // We make the assumption that 'equals' is cheaper than 121 // 'three_way_compare'. 122 if (Element::equals(lhs + offset, rhs + offset)) 123 continue; 124 return Element::three_way_compare(lhs + offset, rhs + offset); 125 } 126 return 0; 127 } 128 129 static void splat_set(char *dst, const unsigned char value) { 130 for (size_t i = 0; i < ElementCount; ++i) { 131 const size_t offset = i * Element::SIZE; 132 Element::splat_set(dst + offset, value); 133 } 134 } 135 136 static Storage<Repeated> load(const char *ptr) { 137 Storage<Repeated> value; 138 copy(reinterpret_cast<char *>(&value), ptr); 139 return value; 140 } 141 142 static void store(char *ptr, Storage<Repeated> value) { 143 copy(ptr, reinterpret_cast<const char *>(&value)); 144 } 145 }; 146 147 template <typename Element> struct Repeated<Element, 0> { 148 static void move(char *dst, const char *src) {} 149 }; 150 151 // Chain the operation of several types. 152 // For instance, to handle a 3 bytes operation, one can use: 153 // Chained<UINT16, UINT8>::Operation(); 154 template <typename... Types> struct Chained; 155 156 template <typename Head, typename... Tail> struct Chained<Head, Tail...> { 157 static constexpr size_t SIZE = Head::SIZE + Chained<Tail...>::SIZE; 158 159 static void copy(char *__restrict dst, const char *__restrict src) { 160 Chained<Tail...>::copy(dst + Head::SIZE, src + Head::SIZE); 161 __llvm_libc::copy<Head>(dst, src); 162 } 163 164 static void move(char *dst, const char *src) { 165 const auto value = Head::load(src); 166 Chained<Tail...>::move(dst + Head::SIZE, src + Head::SIZE); 167 Head::store(dst, value); 168 } 169 170 static bool equals(const char *lhs, const char *rhs) { 171 if (!__llvm_libc::equals<Head>(lhs, rhs)) 172 return false; 173 return Chained<Tail...>::equals(lhs + Head::SIZE, rhs + Head::SIZE); 174 } 175 176 static int three_way_compare(const char *lhs, const char *rhs) { 177 if (__llvm_libc::equals<Head>(lhs, rhs)) 178 return Chained<Tail...>::three_way_compare(lhs + Head::SIZE, 179 rhs + Head::SIZE); 180 return __llvm_libc::three_way_compare<Head>(lhs, rhs); 181 } 182 183 static void splat_set(char *dst, const unsigned char value) { 184 Chained<Tail...>::splat_set(dst + Head::SIZE, value); 185 __llvm_libc::splat_set<Head>(dst, value); 186 } 187 }; 188 189 template <> struct Chained<> { 190 static constexpr size_t SIZE = 0; 191 static void copy(char *__restrict dst, const char *__restrict src) {} 192 static void move(char *dst, const char *src) {} 193 static bool equals(const char *lhs, const char *rhs) { return true; } 194 static int three_way_compare(const char *lhs, const char *rhs) { return 0; } 195 static void splat_set(char *dst, const unsigned char value) {} 196 }; 197 198 // Overlap ElementA and ElementB so they span Size bytes. 199 template <size_t Size, typename ElementA, typename ElementB = ElementA> 200 struct Overlap { 201 static constexpr size_t SIZE = Size; 202 static_assert(ElementB::SIZE <= ElementA::SIZE, "ElementB too big"); 203 static_assert(ElementA::SIZE <= Size, "ElementA too big"); 204 static_assert((ElementA::SIZE + ElementB::SIZE) >= Size, 205 "Elements too small to overlap"); 206 static constexpr size_t OFFSET = SIZE - ElementB::SIZE; 207 208 static void copy(char *__restrict dst, const char *__restrict src) { 209 ElementA::copy(dst, src); 210 ElementB::copy(dst + OFFSET, src + OFFSET); 211 } 212 213 static void move(char *dst, const char *src) { 214 const auto value_a = ElementA::load(src); 215 const auto value_b = ElementB::load(src + OFFSET); 216 ElementB::store(dst + OFFSET, value_b); 217 ElementA::store(dst, value_a); 218 } 219 220 static bool equals(const char *lhs, const char *rhs) { 221 if (!ElementA::equals(lhs, rhs)) 222 return false; 223 if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET)) 224 return false; 225 return true; 226 } 227 228 static int three_way_compare(const char *lhs, const char *rhs) { 229 if (!ElementA::equals(lhs, rhs)) 230 return ElementA::three_way_compare(lhs, rhs); 231 if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET)) 232 return ElementB::three_way_compare(lhs + OFFSET, rhs + OFFSET); 233 return 0; 234 } 235 236 static void splat_set(char *dst, const unsigned char value) { 237 ElementA::splat_set(dst, value); 238 ElementB::splat_set(dst + OFFSET, value); 239 } 240 }; 241 242 // Runtime-size Higher-Order Operations 243 // ------------------------------------ 244 // - Tail<T>: Perform the operation on the last 'T::SIZE' bytes of the buffer. 245 // - HeadTail<T>: Perform the operation on the first and last 'T::SIZE' bytes 246 // of the buffer. 247 // - Loop<T>: Perform a loop of fixed-sized operations. 248 249 // Perform the operation on the last 'T::SIZE' bytes of the buffer. 250 // 251 // e.g. with 252 // [1234567812345678123] 253 // [__XXXXXXXXXXXXXX___] 254 // [________XXXXXXXX___] 255 // 256 // Precondition: `size >= T::SIZE`. 257 template <typename T> struct Tail { 258 static void copy(char *__restrict dst, const char *__restrict src, 259 size_t size) { 260 return T::copy(dst + offset(size), src + offset(size)); 261 } 262 263 static bool equals(const char *lhs, const char *rhs, size_t size) { 264 return T::equals(lhs + offset(size), rhs + offset(size)); 265 } 266 267 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 268 return T::three_way_compare(lhs + offset(size), rhs + offset(size)); 269 } 270 271 static void splat_set(char *dst, const unsigned char value, size_t size) { 272 return T::splat_set(dst + offset(size), value); 273 } 274 275 static size_t offset(size_t size) { return size - T::SIZE; } 276 }; 277 278 // Perform the operation on the first and last 'T::SIZE' bytes of the buffer. 279 // This is useful for overlapping operations. 280 // 281 // e.g. with 282 // [1234567812345678123] 283 // [__XXXXXXXXXXXXXX___] 284 // [__XXXXXXXX_________] 285 // [________XXXXXXXX___] 286 // 287 // Precondition: `size >= T::SIZE && size <= 2 x T::SIZE`. 288 template <typename T> struct HeadTail { 289 static void copy(char *__restrict dst, const char *__restrict src, 290 size_t size) { 291 T::copy(dst, src); 292 Tail<T>::copy(dst, src, size); 293 } 294 295 static void move(char *dst, const char *src, size_t size) { 296 const size_t offset = Tail<T>::offset(size); 297 const auto head_value = T::load(src); 298 const auto tail_value = T::load(src + offset); 299 T::store(dst + offset, tail_value); 300 T::store(dst, head_value); 301 } 302 303 static bool equals(const char *lhs, const char *rhs, size_t size) { 304 if (!T::equals(lhs, rhs)) 305 return false; 306 return Tail<T>::equals(lhs, rhs, size); 307 } 308 309 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 310 if (!T::equals(lhs, rhs)) 311 return T::three_way_compare(lhs, rhs); 312 return Tail<T>::three_way_compare(lhs, rhs, size); 313 } 314 315 static void splat_set(char *dst, const unsigned char value, size_t size) { 316 T::splat_set(dst, value); 317 Tail<T>::splat_set(dst, value, size); 318 } 319 }; 320 321 // Simple loop ending with a Tail operation. 322 // 323 // e.g. with 324 // [12345678123456781234567812345678] 325 // [__XXXXXXXXXXXXXXXXXXXXXXXXXXXX___] 326 // [__XXXXXXXX_______________________] 327 // [__________XXXXXXXX_______________] 328 // [__________________XXXXXXXX_______] 329 // [______________________XXXXXXXX___] 330 // 331 // Precondition: 332 // - size >= T::SIZE 333 template <typename T, typename TailT = T> struct Loop { 334 static_assert(T::SIZE == TailT::SIZE, 335 "Tail type must have the same size as T"); 336 337 static void copy(char *__restrict dst, const char *__restrict src, 338 size_t size) { 339 size_t offset = 0; 340 do { 341 T::copy(dst + offset, src + offset); 342 offset += T::SIZE; 343 } while (offset < size - T::SIZE); 344 Tail<TailT>::copy(dst, src, size); 345 } 346 347 // Move forward suitable when dst < src. We load the tail bytes before 348 // handling the loop. 349 // 350 // e.g. Moving two bytes 351 // [ | | | | |] 352 // [___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX___] 353 // [_________________________LLLLLLLL___] 354 // [___LLLLLLLL_________________________] 355 // [_SSSSSSSS___________________________] 356 // [___________LLLLLLLL_________________] 357 // [_________SSSSSSSS___________________] 358 // [___________________LLLLLLLL_________] 359 // [_________________SSSSSSSS___________] 360 // [_______________________SSSSSSSS_____] 361 static void move(char *dst, const char *src, size_t size) { 362 const size_t tail_offset = Tail<T>::offset(size); 363 const auto tail_value = TailT::load(src + tail_offset); 364 size_t offset = 0; 365 do { 366 T::move(dst + offset, src + offset); 367 offset += T::SIZE; 368 } while (offset < size - T::SIZE); 369 TailT::store(dst + tail_offset, tail_value); 370 } 371 372 // Move forward suitable when dst > src. We load the head bytes before 373 // handling the loop. 374 // 375 // e.g. Moving two bytes 376 // [ | | | | |] 377 // [___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX___] 378 // [___LLLLLLLL_________________________] 379 // [_________________________LLLLLLLL___] 380 // [___________________________SSSSSSSS_] 381 // [_________________LLLLLLLL___________] 382 // [___________________SSSSSSSS_________] 383 // [_________LLLLLLLL___________________] 384 // [___________SSSSSSSS_________________] 385 // [_____SSSSSSSS_______________________] 386 static void move_backward(char *dst, const char *src, size_t size) { 387 const auto head_value = TailT::load(src); 388 ptrdiff_t offset = size - T::SIZE; 389 do { 390 T::move(dst + offset, src + offset); 391 offset -= T::SIZE; 392 } while (offset >= 0); 393 TailT::store(dst, head_value); 394 } 395 396 static bool equals(const char *lhs, const char *rhs, size_t size) { 397 size_t offset = 0; 398 do { 399 if (!T::equals(lhs + offset, rhs + offset)) 400 return false; 401 offset += T::SIZE; 402 } while (offset < size - T::SIZE); 403 return Tail<TailT>::equals(lhs, rhs, size); 404 } 405 406 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 407 size_t offset = 0; 408 do { 409 if (!T::equals(lhs + offset, rhs + offset)) 410 return T::three_way_compare(lhs + offset, rhs + offset); 411 offset += T::SIZE; 412 } while (offset < size - T::SIZE); 413 return Tail<TailT>::three_way_compare(lhs, rhs, size); 414 } 415 416 static void splat_set(char *dst, const unsigned char value, size_t size) { 417 size_t offset = 0; 418 do { 419 T::splat_set(dst + offset, value); 420 offset += T::SIZE; 421 } while (offset < size - T::SIZE); 422 Tail<TailT>::splat_set(dst, value, size); 423 } 424 }; 425 426 enum class Arg { _1, _2, Dst = _1, Src = _2, Lhs = _1, Rhs = _2 }; 427 428 namespace internal { 429 430 template <Arg arg> struct ArgSelector {}; 431 432 template <> struct ArgSelector<Arg::_1> { 433 template <typename T1, typename T2> 434 static T1 *__restrict &Select(T1 *__restrict &p1ref, T2 *__restrict &p2ref) { 435 return p1ref; 436 } 437 }; 438 439 template <> struct ArgSelector<Arg::_2> { 440 template <typename T1, typename T2> 441 static T2 *__restrict &Select(T1 *__restrict &p1ref, T2 *__restrict &p2ref) { 442 return p2ref; 443 } 444 }; 445 446 // Provides a specialized bump function that adjusts pointers and size so first 447 // argument (resp. second argument) gets aligned to Alignment. 448 // We make sure the compiler knows about the adjusted pointer alignment. 449 // The 'additional_bumps' parameter allows to reach previous / next aligned 450 // pointers. 451 template <Arg arg, size_t Alignment> struct Align { 452 template <typename T1, typename T2> 453 static void bump(T1 *__restrict &p1ref, T2 *__restrict &p2ref, size_t &size, 454 int additional_bumps = 0) { 455 auto &aligned_ptr = ArgSelector<arg>::Select(p1ref, p2ref); 456 auto offset = offset_to_next_aligned<Alignment>(aligned_ptr); 457 offset += additional_bumps * Alignment; 458 p1ref += offset; 459 p2ref += offset; 460 size -= offset; 461 aligned_ptr = assume_aligned<Alignment>(aligned_ptr); 462 } 463 }; 464 465 } // namespace internal 466 467 // An alignment operation that: 468 // - executes the 'AlignmentT' operation 469 // - bumps 'dst' or 'src' (resp. 'lhs' or 'rhs') pointers so that the selected 470 // pointer gets aligned, size is decreased accordingly. 471 // - calls the 'NextT' operation. 472 // 473 // e.g. A 16-byte Destination Aligned 32-byte Loop Copy can be written as: 474 // copy<Align<_16, Arg::Dst>::Then<Loop<_32>>>(dst, src, count); 475 template <typename AlignmentT, Arg AlignOn = Arg::_1> struct Align { 476 private: 477 static constexpr size_t ALIGNMENT = AlignmentT::SIZE; 478 static_assert(ALIGNMENT > 1, "Alignment must be more than 1"); 479 static_assert(is_power2(ALIGNMENT), "Alignment must be a power of 2"); 480 481 public: 482 template <typename NextT> struct Then { 483 static void copy(char *__restrict dst, const char *__restrict src, 484 size_t size) { 485 AlignmentT::copy(dst, src); 486 internal::Align<AlignOn, ALIGNMENT>::bump(dst, src, size); 487 NextT::copy(dst, src, size); 488 } 489 490 // Move forward suitable when dst < src. The alignment is performed with an 491 // HeadTail operation of size ∈ [Alignment, 2 x Alignment]. 492 // 493 // e.g. Moving two bytes and making sure src is then aligned. 494 // [ | | | | ] 495 // [____XXXXXXXXXXXXXXXXXXXXXXXXXXXX_] 496 // [____LLLLLLLL_____________________] 497 // [___________LLLLLLLL______________] 498 // [_SSSSSSSS________________________] 499 // [________SSSSSSSS_________________] 500 // 501 // e.g. Moving two bytes and making sure dst is then aligned. 502 // [ | | | | ] 503 // [____XXXXXXXXXXXXXXXXXXXXXXXXXXXX_] 504 // [____LLLLLLLL_____________________] 505 // [______LLLLLLLL___________________] 506 // [_SSSSSSSS________________________] 507 // [___SSSSSSSS______________________] 508 static void move(char *dst, const char *src, size_t size) { 509 char *next_dst = dst; 510 const char *next_src = src; 511 size_t next_size = size; 512 internal::Align<AlignOn, ALIGNMENT>::bump(next_dst, next_src, next_size, 513 1); 514 HeadTail<AlignmentT>::move(dst, src, size - next_size); 515 NextT::move(next_dst, next_src, next_size); 516 } 517 518 // Move backward suitable when dst > src. The alignment is performed with an 519 // HeadTail operation of size ∈ [Alignment, 2 x Alignment]. 520 // 521 // e.g. Moving two bytes backward and making sure src is then aligned. 522 // [ | | | | ] 523 // [____XXXXXXXXXXXXXXXXXXXXXXXX_____] 524 // [ _________________LLLLLLLL_______] 525 // [ ___________________LLLLLLLL_____] 526 // [____________________SSSSSSSS_____] 527 // [______________________SSSSSSSS___] 528 // 529 // e.g. Moving two bytes and making sure dst is then aligned. 530 // [ | | | | ] 531 // [____XXXXXXXXXXXXXXXXXXXXXXXX_____] 532 // [ _______________LLLLLLLL_________] 533 // [ ___________________LLLLLLLL_____] 534 // [__________________SSSSSSSS_______] 535 // [______________________SSSSSSSS___] 536 static void move_backward(char *dst, const char *src, size_t size) { 537 char *headtail_dst = dst + size; 538 const char *headtail_src = src + size; 539 size_t headtail_size = 0; 540 internal::Align<AlignOn, ALIGNMENT>::bump(headtail_dst, headtail_src, 541 headtail_size, -2); 542 HeadTail<AlignmentT>::move(headtail_dst, headtail_src, headtail_size); 543 NextT::move_backward(dst, src, size - headtail_size); 544 } 545 546 static bool equals(const char *lhs, const char *rhs, size_t size) { 547 if (!AlignmentT::equals(lhs, rhs)) 548 return false; 549 internal::Align<AlignOn, ALIGNMENT>::bump(lhs, rhs, size); 550 return NextT::equals(lhs, rhs, size); 551 } 552 553 static int three_way_compare(const char *lhs, const char *rhs, 554 size_t size) { 555 if (!AlignmentT::equals(lhs, rhs)) 556 return AlignmentT::three_way_compare(lhs, rhs); 557 internal::Align<AlignOn, ALIGNMENT>::bump(lhs, rhs, size); 558 return NextT::three_way_compare(lhs, rhs, size); 559 } 560 561 static void splat_set(char *dst, const unsigned char value, size_t size) { 562 AlignmentT::splat_set(dst, value); 563 char *dummy = nullptr; 564 internal::Align<Arg::_1, ALIGNMENT>::bump(dst, dummy, size); 565 NextT::splat_set(dst, value, size); 566 } 567 }; 568 }; 569 570 // An operation that allows to skip the specified amount of bytes. 571 template <ptrdiff_t Bytes> struct Skip { 572 template <typename NextT> struct Then { 573 static void copy(char *__restrict dst, const char *__restrict src, 574 size_t size) { 575 NextT::copy(dst + Bytes, src + Bytes, size - Bytes); 576 } 577 578 static void copy(char *__restrict dst, const char *__restrict src) { 579 NextT::copy(dst + Bytes, src + Bytes); 580 } 581 582 static bool equals(const char *lhs, const char *rhs, size_t size) { 583 return NextT::equals(lhs + Bytes, rhs + Bytes, size - Bytes); 584 } 585 586 static bool equals(const char *lhs, const char *rhs) { 587 return NextT::equals(lhs + Bytes, rhs + Bytes); 588 } 589 590 static int three_way_compare(const char *lhs, const char *rhs, 591 size_t size) { 592 return NextT::three_way_compare(lhs + Bytes, rhs + Bytes, size - Bytes); 593 } 594 595 static int three_way_compare(const char *lhs, const char *rhs) { 596 return NextT::three_way_compare(lhs + Bytes, rhs + Bytes); 597 } 598 599 static void splat_set(char *dst, const unsigned char value, size_t size) { 600 NextT::splat_set(dst + Bytes, value, size - Bytes); 601 } 602 603 static void splat_set(char *dst, const unsigned char value) { 604 NextT::splat_set(dst + Bytes, value); 605 } 606 }; 607 }; 608 609 // Fixed-size Builtin Operations 610 // ----------------------------- 611 // Note: Do not use 'builtin' right now as it requires the implementation of the 612 // `_inline` versions of all the builtins. Theoretically, Clang can still turn 613 // them into calls to the C library leading to reentrancy problems. 614 namespace builtin { 615 616 #ifndef __has_builtin 617 #define __has_builtin(x) 0 // Compatibility with non-clang compilers. 618 #endif 619 620 template <size_t Size> struct Builtin { 621 static constexpr size_t SIZE = Size; 622 623 static void copy(char *__restrict dst, const char *__restrict src) { 624 #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER 625 for_loop_copy(dst, src); 626 #elif __has_builtin(__builtin_memcpy_inline) 627 // __builtin_memcpy_inline guarantees to never call external functions. 628 // Unfortunately it is not widely available. 629 __builtin_memcpy_inline(dst, src, SIZE); 630 #else 631 for_loop_copy(dst, src); 632 #endif 633 } 634 635 static void move(char *dst, const char *src) { 636 #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER 637 for_loop_move(dst, src); 638 #elif __has_builtin(__builtin_memmove) 639 __builtin_memmove(dst, src, SIZE); 640 #else 641 for_loop_move(dst, src); 642 #endif 643 } 644 645 #if __has_builtin(__builtin_memcmp_inline) 646 #define LLVM_LIBC_MEMCMP __builtin_memcmp_inline 647 #else 648 #define LLVM_LIBC_MEMCMP __builtin_memcmp 649 #endif 650 651 static bool equals(const char *lhs, const char *rhs) { 652 return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE) == 0; 653 } 654 655 static int three_way_compare(const char *lhs, const char *rhs) { 656 return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE); 657 } 658 659 static void splat_set(char *dst, const unsigned char value) { 660 __builtin_memset(dst, value, SIZE); 661 } 662 663 private: 664 // Copies `SIZE` bytes from `src` to `dst` using a for loop. 665 // This code requires the use of `-fno-builtin-memcpy` to prevent the compiler 666 // from turning the for-loop back into `__builtin_memcpy`. 667 static void for_loop_copy(char *__restrict dst, const char *__restrict src) { 668 for (size_t i = 0; i < SIZE; ++i) 669 dst[i] = src[i]; 670 } 671 672 static void for_loop_move(char *dst, const char *src) { 673 for (size_t i = 0; i < SIZE; ++i) 674 dst[i] = src[i]; 675 } 676 }; 677 678 using _1 = Builtin<1>; 679 using _2 = Builtin<2>; 680 using _3 = Builtin<3>; 681 using _4 = Builtin<4>; 682 using _8 = Builtin<8>; 683 using _16 = Builtin<16>; 684 using _32 = Builtin<32>; 685 using _64 = Builtin<64>; 686 using _128 = Builtin<128>; 687 688 } // namespace builtin 689 690 // Fixed-size Scalar Operations 691 // ---------------------------- 692 namespace scalar { 693 694 // The Scalar type makes use of simple sized integers. 695 template <typename T> struct Scalar { 696 static constexpr size_t SIZE = sizeof(T); 697 698 static void copy(char *__restrict dst, const char *__restrict src) { 699 store(dst, load(src)); 700 } 701 702 static void move(char *dst, const char *src) { store(dst, load(src)); } 703 704 static bool equals(const char *lhs, const char *rhs) { 705 return load(lhs) == load(rhs); 706 } 707 708 static int three_way_compare(const char *lhs, const char *rhs) { 709 return scalar_three_way_compare(load(lhs), load(rhs)); 710 } 711 712 static void splat_set(char *dst, const unsigned char value) { 713 store(dst, get_splatted_value(value)); 714 } 715 716 static int scalar_three_way_compare(T a, T b); 717 718 static T load(const char *ptr) { 719 T value; 720 builtin::Builtin<SIZE>::copy(reinterpret_cast<char *>(&value), ptr); 721 return value; 722 } 723 static void store(char *ptr, T value) { 724 builtin::Builtin<SIZE>::copy(ptr, reinterpret_cast<const char *>(&value)); 725 } 726 727 private: 728 static T get_splatted_value(const unsigned char value) { 729 return T(~0) / T(0xFF) * T(value); 730 } 731 }; 732 733 template <> 734 inline int Scalar<uint8_t>::scalar_three_way_compare(uint8_t a, uint8_t b) { 735 const int16_t la = Endian::to_big_endian(a); 736 const int16_t lb = Endian::to_big_endian(b); 737 return la - lb; 738 } 739 template <> 740 inline int Scalar<uint16_t>::scalar_three_way_compare(uint16_t a, uint16_t b) { 741 const int32_t la = Endian::to_big_endian(a); 742 const int32_t lb = Endian::to_big_endian(b); 743 return la - lb; 744 } 745 template <> 746 inline int Scalar<uint32_t>::scalar_three_way_compare(uint32_t a, uint32_t b) { 747 const uint32_t la = Endian::to_big_endian(a); 748 const uint32_t lb = Endian::to_big_endian(b); 749 return la > lb ? 1 : la < lb ? -1 : 0; 750 } 751 template <> 752 inline int Scalar<uint64_t>::scalar_three_way_compare(uint64_t a, uint64_t b) { 753 const uint64_t la = Endian::to_big_endian(a); 754 const uint64_t lb = Endian::to_big_endian(b); 755 return la > lb ? 1 : la < lb ? -1 : 0; 756 } 757 758 using UINT8 = Scalar<uint8_t>; // 1 Byte 759 using UINT16 = Scalar<uint16_t>; // 2 Bytes 760 using UINT32 = Scalar<uint32_t>; // 4 Bytes 761 using UINT64 = Scalar<uint64_t>; // 8 Bytes 762 763 using _1 = UINT8; 764 using _2 = UINT16; 765 using _3 = Chained<UINT16, UINT8>; 766 using _4 = UINT32; 767 using _8 = UINT64; 768 using _16 = Repeated<_8, 2>; 769 using _32 = Repeated<_8, 4>; 770 using _64 = Repeated<_8, 8>; 771 using _128 = Repeated<_8, 16>; 772 773 } // namespace scalar 774 } // namespace __llvm_libc 775 776 #include <src/string/memory_utils/elements_aarch64.h> 777 #include <src/string/memory_utils/elements_x86.h> 778 779 #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H 780