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 47 // Fixed-size equality between 'lhs' and 'rhs'. 48 template <typename Element> bool equals(const char *lhs, const char *rhs) { 49 return Element::equals(lhs, rhs); 50 } 51 // Runtime-size equality between 'lhs' and 'rhs'. 52 template <typename Element> 53 bool equals(const char *lhs, const char *rhs, size_t size) { 54 return Element::equals(lhs, rhs, size); 55 } 56 57 // Fixed-size three-way comparison between 'lhs' and 'rhs'. 58 template <typename Element> 59 int three_way_compare(const char *lhs, const char *rhs) { 60 return Element::three_way_compare(lhs, rhs); 61 } 62 // Runtime-size three-way comparison between 'lhs' and 'rhs'. 63 template <typename Element> 64 int three_way_compare(const char *lhs, const char *rhs, size_t size) { 65 return Element::three_way_compare(lhs, rhs, size); 66 } 67 68 // Fixed-size initialization. 69 template <typename Element> 70 void splat_set(char *dst, const unsigned char value) { 71 Element::splat_set(dst, value); 72 } 73 // Runtime-size initialization. 74 template <typename Element> 75 void splat_set(char *dst, const unsigned char value, size_t size) { 76 Element::splat_set(dst, value, size); 77 } 78 79 // Stack placeholder for Move operations. 80 template <typename Element> struct Storage { char bytes[Element::SIZE]; }; 81 82 // Fixed-size Higher-Order Operations 83 // ---------------------------------- 84 // - Repeated<Type, ElementCount>: Repeat the operation several times in a row. 85 // - Chained<Types...>: Chain the operation of several types. 86 87 // Repeat the operation several times in a row. 88 template <typename Element, size_t ElementCount> struct Repeated { 89 static constexpr size_t SIZE = ElementCount * Element::SIZE; 90 91 static void copy(char *__restrict dst, const char *__restrict src) { 92 for (size_t i = 0; i < ElementCount; ++i) { 93 const size_t offset = i * Element::SIZE; 94 Element::copy(dst + offset, src + offset); 95 } 96 } 97 98 static void move(char *dst, const char *src) { 99 const auto value = Element::load(src); 100 Repeated<Element, ElementCount - 1>::move(dst + Element::SIZE, 101 src + Element::SIZE); 102 Element::store(dst, value); 103 } 104 105 static bool equals(const char *lhs, const char *rhs) { 106 for (size_t i = 0; i < ElementCount; ++i) { 107 const size_t offset = i * Element::SIZE; 108 if (!Element::equals(lhs + offset, rhs + offset)) 109 return false; 110 } 111 return true; 112 } 113 114 static int three_way_compare(const char *lhs, const char *rhs) { 115 for (size_t i = 0; i < ElementCount; ++i) { 116 const size_t offset = i * Element::SIZE; 117 // We make the assumption that 'equals' is cheaper than 118 // 'three_way_compare'. 119 if (Element::equals(lhs + offset, rhs + offset)) 120 continue; 121 return Element::three_way_compare(lhs + offset, rhs + offset); 122 } 123 return 0; 124 } 125 126 static void splat_set(char *dst, const unsigned char value) { 127 for (size_t i = 0; i < ElementCount; ++i) { 128 const size_t offset = i * Element::SIZE; 129 Element::splat_set(dst + offset, value); 130 } 131 } 132 133 static Storage<Repeated> load(const char *ptr) { 134 Storage<Repeated> value; 135 copy(reinterpret_cast<char *>(&value), ptr); 136 return value; 137 } 138 139 static void store(char *ptr, Storage<Repeated> value) { 140 copy(ptr, reinterpret_cast<const char *>(&value)); 141 } 142 }; 143 144 template <typename Element> struct Repeated<Element, 0> { 145 static void move(char *dst, const char *src) {} 146 }; 147 148 // Chain the operation of several types. 149 // For instance, to handle a 3 bytes operation, one can use: 150 // Chained<UINT16, UINT8>::Operation(); 151 template <typename... Types> struct Chained; 152 153 template <typename Head, typename... Tail> struct Chained<Head, Tail...> { 154 static constexpr size_t SIZE = Head::SIZE + Chained<Tail...>::SIZE; 155 156 static void copy(char *__restrict dst, const char *__restrict src) { 157 Chained<Tail...>::copy(dst + Head::SIZE, src + Head::SIZE); 158 __llvm_libc::copy<Head>(dst, src); 159 } 160 161 static void move(char *dst, const char *src) { 162 const auto value = Head::load(src); 163 Chained<Tail...>::move(dst + Head::SIZE, src + Head::SIZE); 164 Head::store(dst, value); 165 } 166 167 static bool equals(const char *lhs, const char *rhs) { 168 if (!__llvm_libc::equals<Head>(lhs, rhs)) 169 return false; 170 return Chained<Tail...>::equals(lhs + Head::SIZE, rhs + Head::SIZE); 171 } 172 173 static int three_way_compare(const char *lhs, const char *rhs) { 174 if (__llvm_libc::equals<Head>(lhs, rhs)) 175 return Chained<Tail...>::three_way_compare(lhs + Head::SIZE, 176 rhs + Head::SIZE); 177 return __llvm_libc::three_way_compare<Head>(lhs, rhs); 178 } 179 180 static void splat_set(char *dst, const unsigned char value) { 181 Chained<Tail...>::splat_set(dst + Head::SIZE, value); 182 __llvm_libc::splat_set<Head>(dst, value); 183 } 184 }; 185 186 template <> struct Chained<> { 187 static constexpr size_t SIZE = 0; 188 static void copy(char *__restrict dst, const char *__restrict src) {} 189 static void move(char *dst, const char *src) {} 190 static bool equals(const char *lhs, const char *rhs) { return true; } 191 static int three_way_compare(const char *lhs, const char *rhs) { return 0; } 192 static void splat_set(char *dst, const unsigned char value) {} 193 }; 194 195 // Overlap ElementA and ElementB so they span Size bytes. 196 template <size_t Size, typename ElementA, typename ElementB = ElementA> 197 struct Overlap { 198 static constexpr size_t SIZE = Size; 199 static_assert(ElementB::SIZE <= ElementA::SIZE, "ElementB too big"); 200 static_assert(ElementA::SIZE <= Size, "ElementA too big"); 201 static_assert((ElementA::SIZE + ElementB::SIZE) >= Size, 202 "Elements too small to overlap"); 203 static constexpr size_t OFFSET = SIZE - ElementB::SIZE; 204 205 static void copy(char *__restrict dst, const char *__restrict src) { 206 ElementA::copy(dst, src); 207 ElementB::copy(dst + OFFSET, src + OFFSET); 208 } 209 210 static void move(char *dst, const char *src) { 211 const auto value_a = ElementA::load(src); 212 const auto value_b = ElementB::load(src + OFFSET); 213 ElementB::store(dst + OFFSET, value_b); 214 ElementA::store(dst, value_a); 215 } 216 217 static bool equals(const char *lhs, const char *rhs) { 218 if (!ElementA::equals(lhs, rhs)) 219 return false; 220 if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET)) 221 return false; 222 return true; 223 } 224 225 static int three_way_compare(const char *lhs, const char *rhs) { 226 if (!ElementA::equals(lhs, rhs)) 227 return ElementA::three_way_compare(lhs, rhs); 228 if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET)) 229 return ElementB::three_way_compare(lhs + OFFSET, rhs + OFFSET); 230 return 0; 231 } 232 233 static void splat_set(char *dst, const unsigned char value) { 234 ElementA::splat_set(dst, value); 235 ElementB::splat_set(dst + OFFSET, value); 236 } 237 }; 238 239 // Runtime-size Higher-Order Operations 240 // ------------------------------------ 241 // - Tail<T>: Perform the operation on the last 'T::SIZE' bytes of the buffer. 242 // - HeadTail<T>: Perform the operation on the first and last 'T::SIZE' bytes 243 // of the buffer. 244 // - Loop<T>: Perform a loop of fixed-sized operations. 245 246 // Perform the operation on the last 'T::SIZE' bytes of the buffer. 247 // 248 // e.g. with 249 // [1234567812345678123] 250 // [__XXXXXXXXXXXXXX___] 251 // [________XXXXXXXX___] 252 // 253 // Precondition: `size >= T::SIZE`. 254 template <typename T> struct Tail { 255 static void copy(char *__restrict dst, const char *__restrict src, 256 size_t size) { 257 return T::copy(dst + offset(size), src + offset(size)); 258 } 259 260 static bool equals(const char *lhs, const char *rhs, size_t size) { 261 return T::equals(lhs + offset(size), rhs + offset(size)); 262 } 263 264 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 265 return T::three_way_compare(lhs + offset(size), rhs + offset(size)); 266 } 267 268 static void splat_set(char *dst, const unsigned char value, size_t size) { 269 return T::splat_set(dst + offset(size), value); 270 } 271 272 static size_t offset(size_t size) { return size - T::SIZE; } 273 }; 274 275 // Perform the operation on the first and last 'T::SIZE' bytes of the buffer. 276 // This is useful for overlapping operations. 277 // 278 // e.g. with 279 // [1234567812345678123] 280 // [__XXXXXXXXXXXXXX___] 281 // [__XXXXXXXX_________] 282 // [________XXXXXXXX___] 283 // 284 // Precondition: `size >= T::SIZE && size <= 2 x T::SIZE`. 285 template <typename T> struct HeadTail { 286 static void copy(char *__restrict dst, const char *__restrict src, 287 size_t size) { 288 T::copy(dst, src); 289 Tail<T>::copy(dst, src, size); 290 } 291 292 static void move(char *dst, const char *src, size_t size) { 293 const size_t offset = Tail<T>::offset(size); 294 const auto head_value = T::load(src); 295 const auto tail_value = T::load(src + offset); 296 T::store(dst + offset, tail_value); 297 T::store(dst, head_value); 298 } 299 300 static bool equals(const char *lhs, const char *rhs, size_t size) { 301 if (!T::equals(lhs, rhs)) 302 return false; 303 return Tail<T>::equals(lhs, rhs, size); 304 } 305 306 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 307 if (!T::equals(lhs, rhs)) 308 return T::three_way_compare(lhs, rhs); 309 return Tail<T>::three_way_compare(lhs, rhs, size); 310 } 311 312 static void splat_set(char *dst, const unsigned char value, size_t size) { 313 T::splat_set(dst, value); 314 Tail<T>::splat_set(dst, value, size); 315 } 316 }; 317 318 // Simple loop ending with a Tail operation. 319 // 320 // e.g. with 321 // [12345678123456781234567812345678] 322 // [__XXXXXXXXXXXXXXXXXXXXXXXXXXXX___] 323 // [__XXXXXXXX_______________________] 324 // [__________XXXXXXXX_______________] 325 // [__________________XXXXXXXX_______] 326 // [______________________XXXXXXXX___] 327 // 328 // Precondition: 329 // - size >= T::SIZE 330 template <typename T, typename TailT = T> struct Loop { 331 static_assert(T::SIZE == TailT::SIZE, 332 "Tail type must have the same size as T"); 333 334 static void copy(char *__restrict dst, const char *__restrict src, 335 size_t size) { 336 size_t offset = 0; 337 do { 338 T::copy(dst + offset, src + offset); 339 offset += T::SIZE; 340 } while (offset < size - T::SIZE); 341 Tail<TailT>::copy(dst, src, size); 342 } 343 344 static bool equals(const char *lhs, const char *rhs, size_t size) { 345 size_t offset = 0; 346 do { 347 if (!T::equals(lhs + offset, rhs + offset)) 348 return false; 349 offset += T::SIZE; 350 } while (offset < size - T::SIZE); 351 return Tail<TailT>::equals(lhs, rhs, size); 352 } 353 354 static int three_way_compare(const char *lhs, const char *rhs, size_t size) { 355 size_t offset = 0; 356 do { 357 if (!T::equals(lhs + offset, rhs + offset)) 358 return T::three_way_compare(lhs + offset, rhs + offset); 359 offset += T::SIZE; 360 } while (offset < size - T::SIZE); 361 return Tail<TailT>::three_way_compare(lhs, rhs, size); 362 } 363 364 static void splat_set(char *dst, const unsigned char value, size_t size) { 365 size_t offset = 0; 366 do { 367 T::splat_set(dst + offset, value); 368 offset += T::SIZE; 369 } while (offset < size - T::SIZE); 370 Tail<TailT>::splat_set(dst, value, size); 371 } 372 }; 373 374 enum class Arg { _1, _2, Dst = _1, Src = _2, Lhs = _1, Rhs = _2 }; 375 376 namespace internal { 377 378 // Provides a specialized bump function that adjusts pointers and size so first 379 // argument (resp. second argument) gets aligned to Alignment. 380 // We make sure the compiler knows about the adjusted pointer alignment. 381 template <Arg arg, size_t Alignment> struct AlignHelper {}; 382 383 template <size_t Alignment> struct AlignHelper<Arg::_1, Alignment> { 384 template <typename T1, typename T2> 385 static void bump(T1 *__restrict &p1ref, T2 *__restrict &p2ref, size_t &size) { 386 const intptr_t offset = offset_to_next_aligned<Alignment>(p1ref); 387 p1ref += offset; 388 p2ref += offset; 389 size -= offset; 390 p1ref = assume_aligned<Alignment>(p1ref); 391 } 392 }; 393 394 template <size_t Alignment> struct AlignHelper<Arg::_2, Alignment> { 395 template <typename T1, typename T2> 396 static void bump(T1 *__restrict &p1ref, T2 *__restrict &p2ref, size_t &size) { 397 const intptr_t offset = offset_to_next_aligned<Alignment>(p2ref); 398 p1ref += offset; 399 p2ref += offset; 400 size -= offset; 401 p2ref = assume_aligned<Alignment>(p2ref); 402 } 403 }; 404 405 } // namespace internal 406 407 // An alignment operation that: 408 // - executes the 'AlignmentT' operation 409 // - bumps 'dst' or 'src' (resp. 'lhs' or 'rhs') pointers so that the selected 410 // pointer gets aligned, size is decreased accordingly. 411 // - calls the 'NextT' operation. 412 // 413 // e.g. A 16-byte Destination Aligned 32-byte Loop Copy can be written as: 414 // copy<Align<_16, Arg::Dst>::Then<Loop<_32>>>(dst, src, count); 415 template <typename AlignmentT, Arg AlignOn = Arg::_1> struct Align { 416 private: 417 static constexpr size_t ALIGNMENT = AlignmentT::SIZE; 418 static_assert(ALIGNMENT > 1, "Alignment must be more than 1"); 419 static_assert(is_power2(ALIGNMENT), "Alignment must be a power of 2"); 420 421 public: 422 template <typename NextT> struct Then { 423 static void copy(char *__restrict dst, const char *__restrict src, 424 size_t size) { 425 AlignmentT::copy(dst, src); 426 internal::AlignHelper<AlignOn, ALIGNMENT>::bump(dst, src, size); 427 NextT::copy(dst, src, size); 428 } 429 430 static bool equals(const char *lhs, const char *rhs, size_t size) { 431 if (!AlignmentT::equals(lhs, rhs)) 432 return false; 433 internal::AlignHelper<AlignOn, ALIGNMENT>::bump(lhs, rhs, size); 434 return NextT::equals(lhs, rhs, size); 435 } 436 437 static int three_way_compare(const char *lhs, const char *rhs, 438 size_t size) { 439 if (!AlignmentT::equals(lhs, rhs)) 440 return AlignmentT::three_way_compare(lhs, rhs); 441 internal::AlignHelper<AlignOn, ALIGNMENT>::bump(lhs, rhs, size); 442 return NextT::three_way_compare(lhs, rhs, size); 443 } 444 445 static void splat_set(char *dst, const unsigned char value, size_t size) { 446 AlignmentT::splat_set(dst, value); 447 char *dummy = nullptr; 448 internal::AlignHelper<Arg::_1, ALIGNMENT>::bump(dst, dummy, size); 449 NextT::splat_set(dst, value, size); 450 } 451 }; 452 }; 453 454 // An operation that allows to skip the specified amount of bytes. 455 template <ptrdiff_t Bytes> struct Skip { 456 template <typename NextT> struct Then { 457 static void copy(char *__restrict dst, const char *__restrict src, 458 size_t size) { 459 NextT::copy(dst + Bytes, src + Bytes, size - Bytes); 460 } 461 462 static void copy(char *__restrict dst, const char *__restrict src) { 463 NextT::copy(dst + Bytes, src + Bytes); 464 } 465 466 static bool equals(const char *lhs, const char *rhs, size_t size) { 467 return NextT::equals(lhs + Bytes, rhs + Bytes, size - Bytes); 468 } 469 470 static bool equals(const char *lhs, const char *rhs) { 471 return NextT::equals(lhs + Bytes, rhs + Bytes); 472 } 473 474 static int three_way_compare(const char *lhs, const char *rhs, 475 size_t size) { 476 return NextT::three_way_compare(lhs + Bytes, rhs + Bytes, size - Bytes); 477 } 478 479 static int three_way_compare(const char *lhs, const char *rhs) { 480 return NextT::three_way_compare(lhs + Bytes, rhs + Bytes); 481 } 482 483 static void splat_set(char *dst, const unsigned char value, size_t size) { 484 NextT::splat_set(dst + Bytes, value, size - Bytes); 485 } 486 487 static void splat_set(char *dst, const unsigned char value) { 488 NextT::splat_set(dst + Bytes, value); 489 } 490 }; 491 }; 492 493 // Fixed-size Builtin Operations 494 // ----------------------------- 495 // Note: Do not use 'builtin' right now as it requires the implementation of the 496 // `_inline` versions of all the builtins. Theoretically, Clang can still turn 497 // them into calls to the C library leading to reentrancy problems. 498 namespace builtin { 499 500 #ifndef __has_builtin 501 #define __has_builtin(x) 0 // Compatibility with non-clang compilers. 502 #endif 503 504 template <size_t Size> struct Builtin { 505 static constexpr size_t SIZE = Size; 506 507 static void copy(char *__restrict dst, const char *__restrict src) { 508 #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER 509 for_loop_copy(dst, src); 510 #elif __has_builtin(__builtin_memcpy_inline) 511 // __builtin_memcpy_inline guarantees to never call external functions. 512 // Unfortunately it is not widely available. 513 __builtin_memcpy_inline(dst, src, SIZE); 514 #elif __has_builtin(__builtin_memcpy) 515 __builtin_memcpy(dst, src, SIZE); 516 #else 517 for_loop_copy(dst, src); 518 #endif 519 } 520 521 static void move(char *dst, const char *src) { 522 #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER 523 for_loop_move(dst, src); 524 #elif __has_builtin(__builtin_memmove) 525 __builtin_memmove(dst, src, SIZE); 526 #else 527 for_loop_move(dst, src); 528 #endif 529 } 530 531 #if __has_builtin(__builtin_memcmp_inline) 532 #define LLVM_LIBC_MEMCMP __builtin_memcmp_inline 533 #else 534 #define LLVM_LIBC_MEMCMP __builtin_memcmp 535 #endif 536 537 static bool equals(const char *lhs, const char *rhs) { 538 return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE) == 0; 539 } 540 541 static int three_way_compare(const char *lhs, const char *rhs) { 542 return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE); 543 } 544 545 static void splat_set(char *dst, const unsigned char value) { 546 __builtin_memset(dst, value, SIZE); 547 } 548 549 private: 550 // Copies `SIZE` bytes from `src` to `dst` using a for loop. 551 // This code requires the use of `-fno-builtin-memcpy` to prevent the compiler 552 // from turning the for-loop back into `__builtin_memcpy`. 553 static void for_loop_copy(char *__restrict dst, const char *__restrict src) { 554 for (size_t i = 0; i < SIZE; ++i) 555 dst[i] = src[i]; 556 } 557 558 static void for_loop_move(char *dst, const char *src) { 559 for (size_t i = 0; i < SIZE; ++i) 560 dst[i] = src[i]; 561 } 562 }; 563 564 using _1 = Builtin<1>; 565 using _2 = Builtin<2>; 566 using _3 = Builtin<3>; 567 using _4 = Builtin<4>; 568 using _8 = Builtin<8>; 569 using _16 = Builtin<16>; 570 using _32 = Builtin<32>; 571 using _64 = Builtin<64>; 572 using _128 = Builtin<128>; 573 574 } // namespace builtin 575 576 // Fixed-size Scalar Operations 577 // ---------------------------- 578 namespace scalar { 579 580 // The Scalar type makes use of simple sized integers. 581 template <typename T> struct Scalar { 582 static constexpr size_t SIZE = sizeof(T); 583 584 static void copy(char *__restrict dst, const char *__restrict src) { 585 store(dst, load(src)); 586 } 587 588 static void move(char *dst, const char *src) { store(dst, load(src)); } 589 590 static bool equals(const char *lhs, const char *rhs) { 591 return load(lhs) == load(rhs); 592 } 593 594 static int three_way_compare(const char *lhs, const char *rhs) { 595 return scalar_three_way_compare(load(lhs), load(rhs)); 596 } 597 598 static void splat_set(char *dst, const unsigned char value) { 599 store(dst, get_splatted_value(value)); 600 } 601 602 static int scalar_three_way_compare(T a, T b); 603 604 static T load(const char *ptr) { 605 T value; 606 builtin::Builtin<SIZE>::copy(reinterpret_cast<char *>(&value), ptr); 607 return value; 608 } 609 static void store(char *ptr, T value) { 610 builtin::Builtin<SIZE>::copy(ptr, reinterpret_cast<const char *>(&value)); 611 } 612 613 private: 614 static T get_splatted_value(const unsigned char value) { 615 return T(~0) / T(0xFF) * T(value); 616 } 617 }; 618 619 template <> 620 inline int Scalar<uint8_t>::scalar_three_way_compare(uint8_t a, uint8_t b) { 621 const int16_t la = Endian::to_big_endian(a); 622 const int16_t lb = Endian::to_big_endian(b); 623 return la - lb; 624 } 625 template <> 626 inline int Scalar<uint16_t>::scalar_three_way_compare(uint16_t a, uint16_t b) { 627 const int32_t la = Endian::to_big_endian(a); 628 const int32_t lb = Endian::to_big_endian(b); 629 return la - lb; 630 } 631 template <> 632 inline int Scalar<uint32_t>::scalar_three_way_compare(uint32_t a, uint32_t b) { 633 const uint32_t la = Endian::to_big_endian(a); 634 const uint32_t lb = Endian::to_big_endian(b); 635 return la > lb ? 1 : la < lb ? -1 : 0; 636 } 637 template <> 638 inline int Scalar<uint64_t>::scalar_three_way_compare(uint64_t a, uint64_t b) { 639 const uint64_t la = Endian::to_big_endian(a); 640 const uint64_t lb = Endian::to_big_endian(b); 641 return la > lb ? 1 : la < lb ? -1 : 0; 642 } 643 644 using UINT8 = Scalar<uint8_t>; // 1 Byte 645 using UINT16 = Scalar<uint16_t>; // 2 Bytes 646 using UINT32 = Scalar<uint32_t>; // 4 Bytes 647 using UINT64 = Scalar<uint64_t>; // 8 Bytes 648 649 using _1 = UINT8; 650 using _2 = UINT16; 651 using _3 = Chained<UINT16, UINT8>; 652 using _4 = UINT32; 653 using _8 = UINT64; 654 using _16 = Repeated<_8, 2>; 655 using _32 = Repeated<_8, 4>; 656 using _64 = Repeated<_8, 8>; 657 using _128 = Repeated<_8, 16>; 658 659 } // namespace scalar 660 } // namespace __llvm_libc 661 662 #include <src/string/memory_utils/elements_aarch64.h> 663 #include <src/string/memory_utils/elements_x86.h> 664 665 #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H 666