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