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