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 #else
515     for_loop_copy(dst, src);
516 #endif
517   }
518 
519   static void move(char *dst, const char *src) {
520 #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER
521     for_loop_move(dst, src);
522 #elif __has_builtin(__builtin_memmove)
523     __builtin_memmove(dst, src, SIZE);
524 #else
525     for_loop_move(dst, src);
526 #endif
527   }
528 
529 #if __has_builtin(__builtin_memcmp_inline)
530 #define LLVM_LIBC_MEMCMP __builtin_memcmp_inline
531 #else
532 #define LLVM_LIBC_MEMCMP __builtin_memcmp
533 #endif
534 
535   static bool equals(const char *lhs, const char *rhs) {
536     return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE) == 0;
537   }
538 
539   static int three_way_compare(const char *lhs, const char *rhs) {
540     return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE);
541   }
542 
543   static void splat_set(char *dst, const unsigned char value) {
544     __builtin_memset(dst, value, SIZE);
545   }
546 
547 private:
548   // Copies `SIZE` bytes from `src` to `dst` using a for loop.
549   // This code requires the use of `-fno-builtin-memcpy` to prevent the compiler
550   // from turning the for-loop back into `__builtin_memcpy`.
551   static void for_loop_copy(char *__restrict dst, const char *__restrict src) {
552     for (size_t i = 0; i < SIZE; ++i)
553       dst[i] = src[i];
554   }
555 
556   static void for_loop_move(char *dst, const char *src) {
557     for (size_t i = 0; i < SIZE; ++i)
558       dst[i] = src[i];
559   }
560 };
561 
562 using _1 = Builtin<1>;
563 using _2 = Builtin<2>;
564 using _3 = Builtin<3>;
565 using _4 = Builtin<4>;
566 using _8 = Builtin<8>;
567 using _16 = Builtin<16>;
568 using _32 = Builtin<32>;
569 using _64 = Builtin<64>;
570 using _128 = Builtin<128>;
571 
572 } // namespace builtin
573 
574 // Fixed-size Scalar Operations
575 // ----------------------------
576 namespace scalar {
577 
578 // The Scalar type makes use of simple sized integers.
579 template <typename T> struct Scalar {
580   static constexpr size_t SIZE = sizeof(T);
581 
582   static void copy(char *__restrict dst, const char *__restrict src) {
583     store(dst, load(src));
584   }
585 
586   static void move(char *dst, const char *src) { store(dst, load(src)); }
587 
588   static bool equals(const char *lhs, const char *rhs) {
589     return load(lhs) == load(rhs);
590   }
591 
592   static int three_way_compare(const char *lhs, const char *rhs) {
593     return scalar_three_way_compare(load(lhs), load(rhs));
594   }
595 
596   static void splat_set(char *dst, const unsigned char value) {
597     store(dst, get_splatted_value(value));
598   }
599 
600   static int scalar_three_way_compare(T a, T b);
601 
602   static T load(const char *ptr) {
603     T value;
604     builtin::Builtin<SIZE>::copy(reinterpret_cast<char *>(&value), ptr);
605     return value;
606   }
607   static void store(char *ptr, T value) {
608     builtin::Builtin<SIZE>::copy(ptr, reinterpret_cast<const char *>(&value));
609   }
610 
611 private:
612   static T get_splatted_value(const unsigned char value) {
613     return T(~0) / T(0xFF) * T(value);
614   }
615 };
616 
617 template <>
618 inline int Scalar<uint8_t>::scalar_three_way_compare(uint8_t a, uint8_t b) {
619   const int16_t la = Endian::to_big_endian(a);
620   const int16_t lb = Endian::to_big_endian(b);
621   return la - lb;
622 }
623 template <>
624 inline int Scalar<uint16_t>::scalar_three_way_compare(uint16_t a, uint16_t b) {
625   const int32_t la = Endian::to_big_endian(a);
626   const int32_t lb = Endian::to_big_endian(b);
627   return la - lb;
628 }
629 template <>
630 inline int Scalar<uint32_t>::scalar_three_way_compare(uint32_t a, uint32_t b) {
631   const uint32_t la = Endian::to_big_endian(a);
632   const uint32_t lb = Endian::to_big_endian(b);
633   return la > lb ? 1 : la < lb ? -1 : 0;
634 }
635 template <>
636 inline int Scalar<uint64_t>::scalar_three_way_compare(uint64_t a, uint64_t b) {
637   const uint64_t la = Endian::to_big_endian(a);
638   const uint64_t lb = Endian::to_big_endian(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