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