17fff39d9SGuillaume Chatelet //===-- Elementary operations to compose memory primitives ----------------===//
27fff39d9SGuillaume Chatelet //
37fff39d9SGuillaume Chatelet // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47fff39d9SGuillaume Chatelet // See https://llvm.org/LICENSE.txt for license information.
57fff39d9SGuillaume Chatelet // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67fff39d9SGuillaume Chatelet //
77fff39d9SGuillaume Chatelet //===----------------------------------------------------------------------===//
87fff39d9SGuillaume Chatelet 
97fff39d9SGuillaume Chatelet #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H
107fff39d9SGuillaume Chatelet #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H
117fff39d9SGuillaume Chatelet 
127fff39d9SGuillaume Chatelet #include <stddef.h> // size_t
137fff39d9SGuillaume Chatelet #include <stdint.h> // uint8_t, uint16_t, uint32_t, uint64_t
147fff39d9SGuillaume Chatelet 
157fff39d9SGuillaume Chatelet #include "src/__support/endian.h"
167fff39d9SGuillaume Chatelet #include "src/string/memory_utils/utils.h"
177fff39d9SGuillaume Chatelet 
187fff39d9SGuillaume Chatelet namespace __llvm_libc {
197fff39d9SGuillaume Chatelet 
207fff39d9SGuillaume Chatelet // Elementary Operations
217fff39d9SGuillaume Chatelet // --------------------------------
227fff39d9SGuillaume Chatelet // We define abstract elementary operations acting on fixed chunks of memory.
237fff39d9SGuillaume Chatelet // These are low level building blocks that are meant to be assembled to compose
247fff39d9SGuillaume Chatelet // higher order abstractions. Each function is defined twice: once with
257fff39d9SGuillaume Chatelet // fixed-size operations, and once with runtime-size operations.
267fff39d9SGuillaume Chatelet 
27392da642SGuillaume Chatelet // Fixed-size copy from 'src' to 'dst'.
287fff39d9SGuillaume Chatelet template <typename Element>
copy(char * __restrict dst,const char * __restrict src)291c92911eSMichael Jones void copy(char *__restrict dst, const char *__restrict src) {
301c92911eSMichael Jones   Element::copy(dst, src);
317fff39d9SGuillaume Chatelet }
32392da642SGuillaume Chatelet // Runtime-size copy from 'src' to 'dst'.
337fff39d9SGuillaume Chatelet template <typename Element>
copy(char * __restrict dst,const char * __restrict src,size_t size)341c92911eSMichael Jones void copy(char *__restrict dst, const char *__restrict src, size_t size) {
351c92911eSMichael Jones   Element::copy(dst, src, size);
367fff39d9SGuillaume Chatelet }
377fff39d9SGuillaume Chatelet 
381b927b68SGuillaume Chatelet // Fixed-size move from 'src' to 'dst'.
move(char * dst,const char * src)391c92911eSMichael Jones template <typename Element> void move(char *dst, const char *src) {
401c92911eSMichael Jones   Element::move(dst, src);
411b927b68SGuillaume Chatelet }
421b927b68SGuillaume Chatelet // Runtime-size move from 'src' to 'dst'.
move(char * dst,const char * src,size_t size)431c92911eSMichael Jones template <typename Element> void move(char *dst, const char *src, size_t size) {
441c92911eSMichael Jones   Element::move(dst, src, size);
451b927b68SGuillaume Chatelet }
46*83f9b13dSGuillaume Chatelet // Runtime-size move from 'src' to 'dst'.
47*83f9b13dSGuillaume Chatelet template <typename Element>
move_backward(char * dst,const char * src,size_t size)48*83f9b13dSGuillaume Chatelet void move_backward(char *dst, const char *src, size_t size) {
49*83f9b13dSGuillaume Chatelet   Element::move_backward(dst, src, size);
50*83f9b13dSGuillaume Chatelet }
511b927b68SGuillaume Chatelet 
527fff39d9SGuillaume Chatelet // Fixed-size equality between 'lhs' and 'rhs'.
equals(const char * lhs,const char * rhs)531c92911eSMichael Jones template <typename Element> bool equals(const char *lhs, const char *rhs) {
541c92911eSMichael Jones   return Element::equals(lhs, rhs);
557fff39d9SGuillaume Chatelet }
567fff39d9SGuillaume Chatelet // Runtime-size equality between 'lhs' and 'rhs'.
577fff39d9SGuillaume Chatelet template <typename Element>
equals(const char * lhs,const char * rhs,size_t size)581c92911eSMichael Jones bool equals(const char *lhs, const char *rhs, size_t size) {
591c92911eSMichael Jones   return Element::equals(lhs, rhs, size);
607fff39d9SGuillaume Chatelet }
617fff39d9SGuillaume Chatelet 
627fff39d9SGuillaume Chatelet // Fixed-size three-way comparison between 'lhs' and 'rhs'.
637fff39d9SGuillaume Chatelet template <typename Element>
three_way_compare(const char * lhs,const char * rhs)641c92911eSMichael Jones int three_way_compare(const char *lhs, const char *rhs) {
651c92911eSMichael Jones   return Element::three_way_compare(lhs, rhs);
667fff39d9SGuillaume Chatelet }
677fff39d9SGuillaume Chatelet // Runtime-size three-way comparison between 'lhs' and 'rhs'.
687fff39d9SGuillaume Chatelet template <typename Element>
three_way_compare(const char * lhs,const char * rhs,size_t size)691c92911eSMichael Jones int three_way_compare(const char *lhs, const char *rhs, size_t size) {
701c92911eSMichael Jones   return Element::three_way_compare(lhs, rhs, size);
717fff39d9SGuillaume Chatelet }
727fff39d9SGuillaume Chatelet 
737fff39d9SGuillaume Chatelet // Fixed-size initialization.
747fff39d9SGuillaume Chatelet template <typename Element>
splat_set(char * dst,const unsigned char value)751c92911eSMichael Jones void splat_set(char *dst, const unsigned char value) {
761c92911eSMichael Jones   Element::splat_set(dst, value);
777fff39d9SGuillaume Chatelet }
787fff39d9SGuillaume Chatelet // Runtime-size initialization.
797fff39d9SGuillaume Chatelet template <typename Element>
splat_set(char * dst,const unsigned char value,size_t size)801c92911eSMichael Jones void splat_set(char *dst, const unsigned char value, size_t size) {
811c92911eSMichael Jones   Element::splat_set(dst, value, size);
827fff39d9SGuillaume Chatelet }
837fff39d9SGuillaume Chatelet 
841b927b68SGuillaume Chatelet // Stack placeholder for Move operations.
851c92911eSMichael Jones template <typename Element> struct Storage { char bytes[Element::SIZE]; };
861b927b68SGuillaume Chatelet 
877fff39d9SGuillaume Chatelet // Fixed-size Higher-Order Operations
887fff39d9SGuillaume Chatelet // ----------------------------------
897fff39d9SGuillaume Chatelet // - Repeated<Type, ElementCount>: Repeat the operation several times in a row.
907fff39d9SGuillaume Chatelet // - Chained<Types...>: Chain the operation of several types.
917fff39d9SGuillaume Chatelet 
927fff39d9SGuillaume Chatelet // Repeat the operation several times in a row.
937fff39d9SGuillaume Chatelet template <typename Element, size_t ElementCount> struct Repeated {
941c92911eSMichael Jones   static constexpr size_t SIZE = ElementCount * Element::SIZE;
957fff39d9SGuillaume Chatelet 
copyRepeated961c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {
977fff39d9SGuillaume Chatelet     for (size_t i = 0; i < ElementCount; ++i) {
981c92911eSMichael Jones       const size_t offset = i * Element::SIZE;
991c92911eSMichael Jones       Element::copy(dst + offset, src + offset);
1007fff39d9SGuillaume Chatelet     }
1017fff39d9SGuillaume Chatelet   }
1027fff39d9SGuillaume Chatelet 
moveRepeated1031c92911eSMichael Jones   static void move(char *dst, const char *src) {
104*83f9b13dSGuillaume Chatelet     const auto value = load(src);
105*83f9b13dSGuillaume Chatelet     store(dst, value);
1061b927b68SGuillaume Chatelet   }
1071b927b68SGuillaume Chatelet 
equalsRepeated1081c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) {
1097fff39d9SGuillaume Chatelet     for (size_t i = 0; i < ElementCount; ++i) {
1101c92911eSMichael Jones       const size_t offset = i * Element::SIZE;
1111c92911eSMichael Jones       if (!Element::equals(lhs + offset, rhs + offset))
1127fff39d9SGuillaume Chatelet         return false;
1137fff39d9SGuillaume Chatelet     }
1147fff39d9SGuillaume Chatelet     return true;
1157fff39d9SGuillaume Chatelet   }
1167fff39d9SGuillaume Chatelet 
three_way_compareRepeated1171c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) {
1187fff39d9SGuillaume Chatelet     for (size_t i = 0; i < ElementCount; ++i) {
1191c92911eSMichael Jones       const size_t offset = i * Element::SIZE;
1201c92911eSMichael Jones       // We make the assumption that 'equals' is cheaper than
1211c92911eSMichael Jones       // 'three_way_compare'.
1221c92911eSMichael Jones       if (Element::equals(lhs + offset, rhs + offset))
1237fff39d9SGuillaume Chatelet         continue;
1241c92911eSMichael Jones       return Element::three_way_compare(lhs + offset, rhs + offset);
1257fff39d9SGuillaume Chatelet     }
1267fff39d9SGuillaume Chatelet     return 0;
1277fff39d9SGuillaume Chatelet   }
1287fff39d9SGuillaume Chatelet 
splat_setRepeated1291c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {
1307fff39d9SGuillaume Chatelet     for (size_t i = 0; i < ElementCount; ++i) {
1311c92911eSMichael Jones       const size_t offset = i * Element::SIZE;
1321c92911eSMichael Jones       Element::splat_set(dst + offset, value);
1337fff39d9SGuillaume Chatelet     }
1347fff39d9SGuillaume Chatelet   }
1351b927b68SGuillaume Chatelet 
loadRepeated1361c92911eSMichael Jones   static Storage<Repeated> load(const char *ptr) {
137408c0cc4SGuillaume Chatelet     Storage<Repeated> value;
1381c92911eSMichael Jones     copy(reinterpret_cast<char *>(&value), ptr);
1391b927b68SGuillaume Chatelet     return value;
1401b927b68SGuillaume Chatelet   }
1411b927b68SGuillaume Chatelet 
storeRepeated1421c92911eSMichael Jones   static void store(char *ptr, Storage<Repeated> value) {
1431c92911eSMichael Jones     copy(ptr, reinterpret_cast<const char *>(&value));
1441b927b68SGuillaume Chatelet   }
1451b927b68SGuillaume Chatelet };
1461b927b68SGuillaume Chatelet 
1471b927b68SGuillaume Chatelet template <typename Element> struct Repeated<Element, 0> {
1481c92911eSMichael Jones   static void move(char *dst, const char *src) {}
1497fff39d9SGuillaume Chatelet };
1507fff39d9SGuillaume Chatelet 
1517fff39d9SGuillaume Chatelet // Chain the operation of several types.
1527fff39d9SGuillaume Chatelet // For instance, to handle a 3 bytes operation, one can use:
1537fff39d9SGuillaume Chatelet // Chained<UINT16, UINT8>::Operation();
1547fff39d9SGuillaume Chatelet template <typename... Types> struct Chained;
1557fff39d9SGuillaume Chatelet 
1567fff39d9SGuillaume Chatelet template <typename Head, typename... Tail> struct Chained<Head, Tail...> {
1571c92911eSMichael Jones   static constexpr size_t SIZE = Head::SIZE + Chained<Tail...>::SIZE;
1587fff39d9SGuillaume Chatelet 
1591c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {
1601c92911eSMichael Jones     Chained<Tail...>::copy(dst + Head::SIZE, src + Head::SIZE);
1611c92911eSMichael Jones     __llvm_libc::copy<Head>(dst, src);
1627fff39d9SGuillaume Chatelet   }
1637fff39d9SGuillaume Chatelet 
1641c92911eSMichael Jones   static void move(char *dst, const char *src) {
1651c92911eSMichael Jones     const auto value = Head::load(src);
1661c92911eSMichael Jones     Chained<Tail...>::move(dst + Head::SIZE, src + Head::SIZE);
1671c92911eSMichael Jones     Head::store(dst, value);
1681b927b68SGuillaume Chatelet   }
1691b927b68SGuillaume Chatelet 
1701c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) {
1711c92911eSMichael Jones     if (!__llvm_libc::equals<Head>(lhs, rhs))
1727fff39d9SGuillaume Chatelet       return false;
1731c92911eSMichael Jones     return Chained<Tail...>::equals(lhs + Head::SIZE, rhs + Head::SIZE);
1747fff39d9SGuillaume Chatelet   }
1757fff39d9SGuillaume Chatelet 
1761c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) {
1771c92911eSMichael Jones     if (__llvm_libc::equals<Head>(lhs, rhs))
1781c92911eSMichael Jones       return Chained<Tail...>::three_way_compare(lhs + Head::SIZE,
1791c92911eSMichael Jones                                                  rhs + Head::SIZE);
1801c92911eSMichael Jones     return __llvm_libc::three_way_compare<Head>(lhs, rhs);
1817fff39d9SGuillaume Chatelet   }
1827fff39d9SGuillaume Chatelet 
1831c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {
1841c92911eSMichael Jones     Chained<Tail...>::splat_set(dst + Head::SIZE, value);
1851c92911eSMichael Jones     __llvm_libc::splat_set<Head>(dst, value);
1867fff39d9SGuillaume Chatelet   }
1877fff39d9SGuillaume Chatelet };
1887fff39d9SGuillaume Chatelet 
1897fff39d9SGuillaume Chatelet template <> struct Chained<> {
1901c92911eSMichael Jones   static constexpr size_t SIZE = 0;
1911c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {}
1921c92911eSMichael Jones   static void move(char *dst, const char *src) {}
1931c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) { return true; }
1941c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) { return 0; }
1951c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {}
1967fff39d9SGuillaume Chatelet };
1977fff39d9SGuillaume Chatelet 
19800c943a5SGuillaume Chatelet // Overlap ElementA and ElementB so they span Size bytes.
19900c943a5SGuillaume Chatelet template <size_t Size, typename ElementA, typename ElementB = ElementA>
20000c943a5SGuillaume Chatelet struct Overlap {
2011c92911eSMichael Jones   static constexpr size_t SIZE = Size;
2021c92911eSMichael Jones   static_assert(ElementB::SIZE <= ElementA::SIZE, "ElementB too big");
2031c92911eSMichael Jones   static_assert(ElementA::SIZE <= Size, "ElementA too big");
2041c92911eSMichael Jones   static_assert((ElementA::SIZE + ElementB::SIZE) >= Size,
20500c943a5SGuillaume Chatelet                 "Elements too small to overlap");
2061c92911eSMichael Jones   static constexpr size_t OFFSET = SIZE - ElementB::SIZE;
20700c943a5SGuillaume Chatelet 
2081c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {
2091c92911eSMichael Jones     ElementA::copy(dst, src);
2101c92911eSMichael Jones     ElementB::copy(dst + OFFSET, src + OFFSET);
21100c943a5SGuillaume Chatelet   }
21200c943a5SGuillaume Chatelet 
2131c92911eSMichael Jones   static void move(char *dst, const char *src) {
2141c92911eSMichael Jones     const auto value_a = ElementA::load(src);
2151c92911eSMichael Jones     const auto value_b = ElementB::load(src + OFFSET);
2161c92911eSMichael Jones     ElementB::store(dst + OFFSET, value_b);
2171c92911eSMichael Jones     ElementA::store(dst, value_a);
2181b927b68SGuillaume Chatelet   }
2191b927b68SGuillaume Chatelet 
2201c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) {
2211c92911eSMichael Jones     if (!ElementA::equals(lhs, rhs))
22200c943a5SGuillaume Chatelet       return false;
2231c92911eSMichael Jones     if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET))
22400c943a5SGuillaume Chatelet       return false;
22500c943a5SGuillaume Chatelet     return true;
22600c943a5SGuillaume Chatelet   }
22700c943a5SGuillaume Chatelet 
2281c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) {
2291c92911eSMichael Jones     if (!ElementA::equals(lhs, rhs))
2301c92911eSMichael Jones       return ElementA::three_way_compare(lhs, rhs);
2311c92911eSMichael Jones     if (!ElementB::equals(lhs + OFFSET, rhs + OFFSET))
2321c92911eSMichael Jones       return ElementB::three_way_compare(lhs + OFFSET, rhs + OFFSET);
23300c943a5SGuillaume Chatelet     return 0;
23400c943a5SGuillaume Chatelet   }
23500c943a5SGuillaume Chatelet 
2361c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {
2371c92911eSMichael Jones     ElementA::splat_set(dst, value);
2381c92911eSMichael Jones     ElementB::splat_set(dst + OFFSET, value);
23900c943a5SGuillaume Chatelet   }
24000c943a5SGuillaume Chatelet };
24100c943a5SGuillaume Chatelet 
2427fff39d9SGuillaume Chatelet // Runtime-size Higher-Order Operations
2437fff39d9SGuillaume Chatelet // ------------------------------------
2441c92911eSMichael Jones // - Tail<T>: Perform the operation on the last 'T::SIZE' bytes of the buffer.
2451c92911eSMichael Jones // - HeadTail<T>: Perform the operation on the first and last 'T::SIZE' bytes
2467fff39d9SGuillaume Chatelet //   of the buffer.
2477fff39d9SGuillaume Chatelet // - Loop<T>: Perform a loop of fixed-sized operations.
2487fff39d9SGuillaume Chatelet 
2491c92911eSMichael Jones // Perform the operation on the last 'T::SIZE' bytes of the buffer.
2507fff39d9SGuillaume Chatelet //
2517fff39d9SGuillaume Chatelet // e.g. with
2527fff39d9SGuillaume Chatelet // [1234567812345678123]
2537fff39d9SGuillaume Chatelet // [__XXXXXXXXXXXXXX___]
2547fff39d9SGuillaume Chatelet // [________XXXXXXXX___]
2557fff39d9SGuillaume Chatelet //
2561c92911eSMichael Jones // Precondition: `size >= T::SIZE`.
2577fff39d9SGuillaume Chatelet template <typename T> struct Tail {
2581c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src,
2597fff39d9SGuillaume Chatelet                    size_t size) {
2601c92911eSMichael Jones     return T::copy(dst + offset(size), src + offset(size));
2617fff39d9SGuillaume Chatelet   }
2627fff39d9SGuillaume Chatelet 
2631c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs, size_t size) {
2641c92911eSMichael Jones     return T::equals(lhs + offset(size), rhs + offset(size));
2657fff39d9SGuillaume Chatelet   }
2667fff39d9SGuillaume Chatelet 
2671c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs, size_t size) {
2681c92911eSMichael Jones     return T::three_way_compare(lhs + offset(size), rhs + offset(size));
2697fff39d9SGuillaume Chatelet   }
2707fff39d9SGuillaume Chatelet 
2711c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value, size_t size) {
2721c92911eSMichael Jones     return T::splat_set(dst + offset(size), value);
2737fff39d9SGuillaume Chatelet   }
2747fff39d9SGuillaume Chatelet 
2751c92911eSMichael Jones   static size_t offset(size_t size) { return size - T::SIZE; }
2767fff39d9SGuillaume Chatelet };
2777fff39d9SGuillaume Chatelet 
2781c92911eSMichael Jones // Perform the operation on the first and last 'T::SIZE' bytes of the buffer.
2797fff39d9SGuillaume Chatelet // This is useful for overlapping operations.
2807fff39d9SGuillaume Chatelet //
2817fff39d9SGuillaume Chatelet // e.g. with
2827fff39d9SGuillaume Chatelet // [1234567812345678123]
2837fff39d9SGuillaume Chatelet // [__XXXXXXXXXXXXXX___]
2847fff39d9SGuillaume Chatelet // [__XXXXXXXX_________]
2857fff39d9SGuillaume Chatelet // [________XXXXXXXX___]
2867fff39d9SGuillaume Chatelet //
2871c92911eSMichael Jones // Precondition: `size >= T::SIZE && size <= 2 x T::SIZE`.
2887fff39d9SGuillaume Chatelet template <typename T> struct HeadTail {
2891c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src,
2907fff39d9SGuillaume Chatelet                    size_t size) {
2911c92911eSMichael Jones     T::copy(dst, src);
2921c92911eSMichael Jones     Tail<T>::copy(dst, src, size);
2937fff39d9SGuillaume Chatelet   }
2947fff39d9SGuillaume Chatelet 
2951c92911eSMichael Jones   static void move(char *dst, const char *src, size_t size) {
2961b927b68SGuillaume Chatelet     const size_t offset = Tail<T>::offset(size);
2971c92911eSMichael Jones     const auto head_value = T::load(src);
2981c92911eSMichael Jones     const auto tail_value = T::load(src + offset);
2991c92911eSMichael Jones     T::store(dst + offset, tail_value);
3001c92911eSMichael Jones     T::store(dst, head_value);
3011b927b68SGuillaume Chatelet   }
3021b927b68SGuillaume Chatelet 
3031c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs, size_t size) {
3041c92911eSMichael Jones     if (!T::equals(lhs, rhs))
3057fff39d9SGuillaume Chatelet       return false;
3061c92911eSMichael Jones     return Tail<T>::equals(lhs, rhs, size);
3077fff39d9SGuillaume Chatelet   }
3087fff39d9SGuillaume Chatelet 
3091c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs, size_t size) {
3101c92911eSMichael Jones     if (!T::equals(lhs, rhs))
3111c92911eSMichael Jones       return T::three_way_compare(lhs, rhs);
3121c92911eSMichael Jones     return Tail<T>::three_way_compare(lhs, rhs, size);
3137fff39d9SGuillaume Chatelet   }
3147fff39d9SGuillaume Chatelet 
3151c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value, size_t size) {
3161c92911eSMichael Jones     T::splat_set(dst, value);
3171c92911eSMichael Jones     Tail<T>::splat_set(dst, value, size);
3187fff39d9SGuillaume Chatelet   }
3197fff39d9SGuillaume Chatelet };
3207fff39d9SGuillaume Chatelet 
3217fff39d9SGuillaume Chatelet // Simple loop ending with a Tail operation.
3227fff39d9SGuillaume Chatelet //
3237fff39d9SGuillaume Chatelet // e.g. with
3247fff39d9SGuillaume Chatelet // [12345678123456781234567812345678]
3257fff39d9SGuillaume Chatelet // [__XXXXXXXXXXXXXXXXXXXXXXXXXXXX___]
3267fff39d9SGuillaume Chatelet // [__XXXXXXXX_______________________]
3277fff39d9SGuillaume Chatelet // [__________XXXXXXXX_______________]
3287fff39d9SGuillaume Chatelet // [__________________XXXXXXXX_______]
3297fff39d9SGuillaume Chatelet // [______________________XXXXXXXX___]
3307fff39d9SGuillaume Chatelet //
3317fff39d9SGuillaume Chatelet // Precondition:
3321c92911eSMichael Jones // - size >= T::SIZE
3338e4efad9SGuillaume Chatelet template <typename T, typename TailT = T> struct Loop {
3341c92911eSMichael Jones   static_assert(T::SIZE == TailT::SIZE,
3358e4efad9SGuillaume Chatelet                 "Tail type must have the same size as T");
3368e4efad9SGuillaume Chatelet 
3371c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src,
3387fff39d9SGuillaume Chatelet                    size_t size) {
3398e4efad9SGuillaume Chatelet     size_t offset = 0;
3408e4efad9SGuillaume Chatelet     do {
3411c92911eSMichael Jones       T::copy(dst + offset, src + offset);
3421c92911eSMichael Jones       offset += T::SIZE;
3431c92911eSMichael Jones     } while (offset < size - T::SIZE);
3441c92911eSMichael Jones     Tail<TailT>::copy(dst, src, size);
3457fff39d9SGuillaume Chatelet   }
3467fff39d9SGuillaume Chatelet 
347*83f9b13dSGuillaume Chatelet   // Move forward suitable when dst < src. We load the tail bytes before
348*83f9b13dSGuillaume Chatelet   // handling the loop.
349*83f9b13dSGuillaume Chatelet   //
350*83f9b13dSGuillaume Chatelet   // e.g. Moving two bytes
351*83f9b13dSGuillaume Chatelet   // [   |       |       |       |       |]
352*83f9b13dSGuillaume Chatelet   // [___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX___]
353*83f9b13dSGuillaume Chatelet   // [_________________________LLLLLLLL___]
354*83f9b13dSGuillaume Chatelet   // [___LLLLLLLL_________________________]
355*83f9b13dSGuillaume Chatelet   // [_SSSSSSSS___________________________]
356*83f9b13dSGuillaume Chatelet   // [___________LLLLLLLL_________________]
357*83f9b13dSGuillaume Chatelet   // [_________SSSSSSSS___________________]
358*83f9b13dSGuillaume Chatelet   // [___________________LLLLLLLL_________]
359*83f9b13dSGuillaume Chatelet   // [_________________SSSSSSSS___________]
360*83f9b13dSGuillaume Chatelet   // [_______________________SSSSSSSS_____]
361*83f9b13dSGuillaume Chatelet   static void move(char *dst, const char *src, size_t size) {
362*83f9b13dSGuillaume Chatelet     const size_t tail_offset = Tail<T>::offset(size);
363*83f9b13dSGuillaume Chatelet     const auto tail_value = TailT::load(src + tail_offset);
364*83f9b13dSGuillaume Chatelet     size_t offset = 0;
365*83f9b13dSGuillaume Chatelet     do {
366*83f9b13dSGuillaume Chatelet       T::move(dst + offset, src + offset);
367*83f9b13dSGuillaume Chatelet       offset += T::SIZE;
368*83f9b13dSGuillaume Chatelet     } while (offset < size - T::SIZE);
369*83f9b13dSGuillaume Chatelet     TailT::store(dst + tail_offset, tail_value);
370*83f9b13dSGuillaume Chatelet   }
371*83f9b13dSGuillaume Chatelet 
372*83f9b13dSGuillaume Chatelet   // Move forward suitable when dst > src. We load the head bytes before
373*83f9b13dSGuillaume Chatelet   // handling the loop.
374*83f9b13dSGuillaume Chatelet   //
375*83f9b13dSGuillaume Chatelet   // e.g. Moving two bytes
376*83f9b13dSGuillaume Chatelet   // [   |       |       |       |       |]
377*83f9b13dSGuillaume Chatelet   // [___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX___]
378*83f9b13dSGuillaume Chatelet   // [___LLLLLLLL_________________________]
379*83f9b13dSGuillaume Chatelet   // [_________________________LLLLLLLL___]
380*83f9b13dSGuillaume Chatelet   // [___________________________SSSSSSSS_]
381*83f9b13dSGuillaume Chatelet   // [_________________LLLLLLLL___________]
382*83f9b13dSGuillaume Chatelet   // [___________________SSSSSSSS_________]
383*83f9b13dSGuillaume Chatelet   // [_________LLLLLLLL___________________]
384*83f9b13dSGuillaume Chatelet   // [___________SSSSSSSS_________________]
385*83f9b13dSGuillaume Chatelet   // [_____SSSSSSSS_______________________]
386*83f9b13dSGuillaume Chatelet   static void move_backward(char *dst, const char *src, size_t size) {
387*83f9b13dSGuillaume Chatelet     const auto head_value = TailT::load(src);
388*83f9b13dSGuillaume Chatelet     ptrdiff_t offset = size - T::SIZE;
389*83f9b13dSGuillaume Chatelet     do {
390*83f9b13dSGuillaume Chatelet       T::move(dst + offset, src + offset);
391*83f9b13dSGuillaume Chatelet       offset -= T::SIZE;
392*83f9b13dSGuillaume Chatelet     } while (offset >= 0);
393*83f9b13dSGuillaume Chatelet     TailT::store(dst, head_value);
394*83f9b13dSGuillaume Chatelet   }
395*83f9b13dSGuillaume Chatelet 
3961c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs, size_t size) {
3978e4efad9SGuillaume Chatelet     size_t offset = 0;
3988e4efad9SGuillaume Chatelet     do {
3991c92911eSMichael Jones       if (!T::equals(lhs + offset, rhs + offset))
4007fff39d9SGuillaume Chatelet         return false;
4011c92911eSMichael Jones       offset += T::SIZE;
4021c92911eSMichael Jones     } while (offset < size - T::SIZE);
4031c92911eSMichael Jones     return Tail<TailT>::equals(lhs, rhs, size);
4047fff39d9SGuillaume Chatelet   }
4057fff39d9SGuillaume Chatelet 
4061c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs, size_t size) {
4078e4efad9SGuillaume Chatelet     size_t offset = 0;
4088e4efad9SGuillaume Chatelet     do {
4091c92911eSMichael Jones       if (!T::equals(lhs + offset, rhs + offset))
4101c92911eSMichael Jones         return T::three_way_compare(lhs + offset, rhs + offset);
4111c92911eSMichael Jones       offset += T::SIZE;
4121c92911eSMichael Jones     } while (offset < size - T::SIZE);
4131c92911eSMichael Jones     return Tail<TailT>::three_way_compare(lhs, rhs, size);
4147fff39d9SGuillaume Chatelet   }
4157fff39d9SGuillaume Chatelet 
4161c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value, size_t size) {
4178e4efad9SGuillaume Chatelet     size_t offset = 0;
4188e4efad9SGuillaume Chatelet     do {
4191c92911eSMichael Jones       T::splat_set(dst + offset, value);
4201c92911eSMichael Jones       offset += T::SIZE;
4211c92911eSMichael Jones     } while (offset < size - T::SIZE);
4221c92911eSMichael Jones     Tail<TailT>::splat_set(dst, value, size);
4237fff39d9SGuillaume Chatelet   }
4247fff39d9SGuillaume Chatelet };
4257fff39d9SGuillaume Chatelet 
4267fff39d9SGuillaume Chatelet enum class Arg { _1, _2, Dst = _1, Src = _2, Lhs = _1, Rhs = _2 };
4277fff39d9SGuillaume Chatelet 
4287fff39d9SGuillaume Chatelet namespace internal {
4297fff39d9SGuillaume Chatelet 
430*83f9b13dSGuillaume Chatelet template <Arg arg> struct ArgSelector {};
4317fff39d9SGuillaume Chatelet 
432*83f9b13dSGuillaume Chatelet template <> struct ArgSelector<Arg::_1> {
4337fff39d9SGuillaume Chatelet   template <typename T1, typename T2>
434*83f9b13dSGuillaume Chatelet   static T1 *__restrict &Select(T1 *__restrict &p1ref, T2 *__restrict &p2ref) {
435*83f9b13dSGuillaume Chatelet     return p1ref;
4367fff39d9SGuillaume Chatelet   }
4377fff39d9SGuillaume Chatelet };
4387fff39d9SGuillaume Chatelet 
439*83f9b13dSGuillaume Chatelet template <> struct ArgSelector<Arg::_2> {
4407fff39d9SGuillaume Chatelet   template <typename T1, typename T2>
441*83f9b13dSGuillaume Chatelet   static T2 *__restrict &Select(T1 *__restrict &p1ref, T2 *__restrict &p2ref) {
442*83f9b13dSGuillaume Chatelet     return p2ref;
443*83f9b13dSGuillaume Chatelet   }
444*83f9b13dSGuillaume Chatelet };
445*83f9b13dSGuillaume Chatelet 
446*83f9b13dSGuillaume Chatelet // Provides a specialized bump function that adjusts pointers and size so first
447*83f9b13dSGuillaume Chatelet // argument (resp. second argument) gets aligned to Alignment.
448*83f9b13dSGuillaume Chatelet // We make sure the compiler knows about the adjusted pointer alignment.
449*83f9b13dSGuillaume Chatelet // The 'additional_bumps' parameter allows to reach previous / next aligned
450*83f9b13dSGuillaume Chatelet // pointers.
451*83f9b13dSGuillaume Chatelet template <Arg arg, size_t Alignment> struct Align {
452*83f9b13dSGuillaume Chatelet   template <typename T1, typename T2>
453*83f9b13dSGuillaume Chatelet   static void bump(T1 *__restrict &p1ref, T2 *__restrict &p2ref, size_t &size,
454*83f9b13dSGuillaume Chatelet                    int additional_bumps = 0) {
455*83f9b13dSGuillaume Chatelet     auto &aligned_ptr = ArgSelector<arg>::Select(p1ref, p2ref);
456*83f9b13dSGuillaume Chatelet     auto offset = offset_to_next_aligned<Alignment>(aligned_ptr);
457*83f9b13dSGuillaume Chatelet     offset += additional_bumps * Alignment;
4587fff39d9SGuillaume Chatelet     p1ref += offset;
4597fff39d9SGuillaume Chatelet     p2ref += offset;
4607fff39d9SGuillaume Chatelet     size -= offset;
461*83f9b13dSGuillaume Chatelet     aligned_ptr = assume_aligned<Alignment>(aligned_ptr);
4627fff39d9SGuillaume Chatelet   }
4637fff39d9SGuillaume Chatelet };
4647fff39d9SGuillaume Chatelet 
4657fff39d9SGuillaume Chatelet } // namespace internal
4667fff39d9SGuillaume Chatelet 
4677fff39d9SGuillaume Chatelet // An alignment operation that:
4687fff39d9SGuillaume Chatelet // - executes the 'AlignmentT' operation
4697fff39d9SGuillaume Chatelet // - bumps 'dst' or 'src' (resp. 'lhs' or 'rhs') pointers so that the selected
4707fff39d9SGuillaume Chatelet //   pointer gets aligned, size is decreased accordingly.
4717fff39d9SGuillaume Chatelet // - calls the 'NextT' operation.
4727fff39d9SGuillaume Chatelet //
4737fff39d9SGuillaume Chatelet // e.g. A 16-byte Destination Aligned 32-byte Loop Copy can be written as:
4741c92911eSMichael Jones // copy<Align<_16, Arg::Dst>::Then<Loop<_32>>>(dst, src, count);
475cd2f5d5bSGuillaume Chatelet template <typename AlignmentT, Arg AlignOn = Arg::_1> struct Align {
4767fff39d9SGuillaume Chatelet private:
4771c92911eSMichael Jones   static constexpr size_t ALIGNMENT = AlignmentT::SIZE;
4781c92911eSMichael Jones   static_assert(ALIGNMENT > 1, "Alignment must be more than 1");
4791c92911eSMichael Jones   static_assert(is_power2(ALIGNMENT), "Alignment must be a power of 2");
4807fff39d9SGuillaume Chatelet 
4817fff39d9SGuillaume Chatelet public:
4827fff39d9SGuillaume Chatelet   template <typename NextT> struct Then {
4831c92911eSMichael Jones     static void copy(char *__restrict dst, const char *__restrict src,
4847fff39d9SGuillaume Chatelet                      size_t size) {
4851c92911eSMichael Jones       AlignmentT::copy(dst, src);
486*83f9b13dSGuillaume Chatelet       internal::Align<AlignOn, ALIGNMENT>::bump(dst, src, size);
4871c92911eSMichael Jones       NextT::copy(dst, src, size);
4887fff39d9SGuillaume Chatelet     }
4897fff39d9SGuillaume Chatelet 
490*83f9b13dSGuillaume Chatelet     // Move forward suitable when dst < src. The alignment is performed with an
491*83f9b13dSGuillaume Chatelet     // HeadTail operation of size ∈ [Alignment, 2 x Alignment].
492*83f9b13dSGuillaume Chatelet     //
493*83f9b13dSGuillaume Chatelet     // e.g. Moving two bytes and making sure src is then aligned.
494*83f9b13dSGuillaume Chatelet     // [  |       |       |       |      ]
495*83f9b13dSGuillaume Chatelet     // [____XXXXXXXXXXXXXXXXXXXXXXXXXXXX_]
496*83f9b13dSGuillaume Chatelet     // [____LLLLLLLL_____________________]
497*83f9b13dSGuillaume Chatelet     // [___________LLLLLLLL______________]
498*83f9b13dSGuillaume Chatelet     // [_SSSSSSSS________________________]
499*83f9b13dSGuillaume Chatelet     // [________SSSSSSSS_________________]
500*83f9b13dSGuillaume Chatelet     //
501*83f9b13dSGuillaume Chatelet     // e.g. Moving two bytes and making sure dst is then aligned.
502*83f9b13dSGuillaume Chatelet     // [  |       |       |       |      ]
503*83f9b13dSGuillaume Chatelet     // [____XXXXXXXXXXXXXXXXXXXXXXXXXXXX_]
504*83f9b13dSGuillaume Chatelet     // [____LLLLLLLL_____________________]
505*83f9b13dSGuillaume Chatelet     // [______LLLLLLLL___________________]
506*83f9b13dSGuillaume Chatelet     // [_SSSSSSSS________________________]
507*83f9b13dSGuillaume Chatelet     // [___SSSSSSSS______________________]
508*83f9b13dSGuillaume Chatelet     static void move(char *dst, const char *src, size_t size) {
509*83f9b13dSGuillaume Chatelet       char *next_dst = dst;
510*83f9b13dSGuillaume Chatelet       const char *next_src = src;
511*83f9b13dSGuillaume Chatelet       size_t next_size = size;
512*83f9b13dSGuillaume Chatelet       internal::Align<AlignOn, ALIGNMENT>::bump(next_dst, next_src, next_size,
513*83f9b13dSGuillaume Chatelet                                                 1);
514*83f9b13dSGuillaume Chatelet       HeadTail<AlignmentT>::move(dst, src, size - next_size);
515*83f9b13dSGuillaume Chatelet       NextT::move(next_dst, next_src, next_size);
516*83f9b13dSGuillaume Chatelet     }
517*83f9b13dSGuillaume Chatelet 
518*83f9b13dSGuillaume Chatelet     // Move backward suitable when dst > src. The alignment is performed with an
519*83f9b13dSGuillaume Chatelet     // HeadTail operation of size ∈ [Alignment, 2 x Alignment].
520*83f9b13dSGuillaume Chatelet     //
521*83f9b13dSGuillaume Chatelet     // e.g. Moving two bytes backward and making sure src is then aligned.
522*83f9b13dSGuillaume Chatelet     // [  |       |       |       |      ]
523*83f9b13dSGuillaume Chatelet     // [____XXXXXXXXXXXXXXXXXXXXXXXX_____]
524*83f9b13dSGuillaume Chatelet     // [ _________________LLLLLLLL_______]
525*83f9b13dSGuillaume Chatelet     // [ ___________________LLLLLLLL_____]
526*83f9b13dSGuillaume Chatelet     // [____________________SSSSSSSS_____]
527*83f9b13dSGuillaume Chatelet     // [______________________SSSSSSSS___]
528*83f9b13dSGuillaume Chatelet     //
529*83f9b13dSGuillaume Chatelet     // e.g. Moving two bytes and making sure dst is then aligned.
530*83f9b13dSGuillaume Chatelet     // [  |       |       |       |      ]
531*83f9b13dSGuillaume Chatelet     // [____XXXXXXXXXXXXXXXXXXXXXXXX_____]
532*83f9b13dSGuillaume Chatelet     // [ _______________LLLLLLLL_________]
533*83f9b13dSGuillaume Chatelet     // [ ___________________LLLLLLLL_____]
534*83f9b13dSGuillaume Chatelet     // [__________________SSSSSSSS_______]
535*83f9b13dSGuillaume Chatelet     // [______________________SSSSSSSS___]
536*83f9b13dSGuillaume Chatelet     static void move_backward(char *dst, const char *src, size_t size) {
537*83f9b13dSGuillaume Chatelet       char *headtail_dst = dst + size;
538*83f9b13dSGuillaume Chatelet       const char *headtail_src = src + size;
539*83f9b13dSGuillaume Chatelet       size_t headtail_size = 0;
540*83f9b13dSGuillaume Chatelet       internal::Align<AlignOn, ALIGNMENT>::bump(headtail_dst, headtail_src,
541*83f9b13dSGuillaume Chatelet                                                 headtail_size, -2);
542*83f9b13dSGuillaume Chatelet       HeadTail<AlignmentT>::move(headtail_dst, headtail_src, headtail_size);
543*83f9b13dSGuillaume Chatelet       NextT::move_backward(dst, src, size - headtail_size);
544*83f9b13dSGuillaume Chatelet     }
545*83f9b13dSGuillaume Chatelet 
5461c92911eSMichael Jones     static bool equals(const char *lhs, const char *rhs, size_t size) {
5471c92911eSMichael Jones       if (!AlignmentT::equals(lhs, rhs))
5487fff39d9SGuillaume Chatelet         return false;
549*83f9b13dSGuillaume Chatelet       internal::Align<AlignOn, ALIGNMENT>::bump(lhs, rhs, size);
5501c92911eSMichael Jones       return NextT::equals(lhs, rhs, size);
5517fff39d9SGuillaume Chatelet     }
5527fff39d9SGuillaume Chatelet 
5531c92911eSMichael Jones     static int three_way_compare(const char *lhs, const char *rhs,
5541c92911eSMichael Jones                                  size_t size) {
5551c92911eSMichael Jones       if (!AlignmentT::equals(lhs, rhs))
5561c92911eSMichael Jones         return AlignmentT::three_way_compare(lhs, rhs);
557*83f9b13dSGuillaume Chatelet       internal::Align<AlignOn, ALIGNMENT>::bump(lhs, rhs, size);
5581c92911eSMichael Jones       return NextT::three_way_compare(lhs, rhs, size);
5597fff39d9SGuillaume Chatelet     }
5607fff39d9SGuillaume Chatelet 
5611c92911eSMichael Jones     static void splat_set(char *dst, const unsigned char value, size_t size) {
5621c92911eSMichael Jones       AlignmentT::splat_set(dst, value);
5637fff39d9SGuillaume Chatelet       char *dummy = nullptr;
564*83f9b13dSGuillaume Chatelet       internal::Align<Arg::_1, ALIGNMENT>::bump(dst, dummy, size);
5651c92911eSMichael Jones       NextT::splat_set(dst, value, size);
5667fff39d9SGuillaume Chatelet     }
5677fff39d9SGuillaume Chatelet   };
5687fff39d9SGuillaume Chatelet };
5697fff39d9SGuillaume Chatelet 
570cd2f5d5bSGuillaume Chatelet // An operation that allows to skip the specified amount of bytes.
571cd2f5d5bSGuillaume Chatelet template <ptrdiff_t Bytes> struct Skip {
572cd2f5d5bSGuillaume Chatelet   template <typename NextT> struct Then {
5731c92911eSMichael Jones     static void copy(char *__restrict dst, const char *__restrict src,
574cd2f5d5bSGuillaume Chatelet                      size_t size) {
5751c92911eSMichael Jones       NextT::copy(dst + Bytes, src + Bytes, size - Bytes);
576cd2f5d5bSGuillaume Chatelet     }
577cd2f5d5bSGuillaume Chatelet 
5781c92911eSMichael Jones     static void copy(char *__restrict dst, const char *__restrict src) {
5791c92911eSMichael Jones       NextT::copy(dst + Bytes, src + Bytes);
580cd2f5d5bSGuillaume Chatelet     }
581cd2f5d5bSGuillaume Chatelet 
5821c92911eSMichael Jones     static bool equals(const char *lhs, const char *rhs, size_t size) {
5831c92911eSMichael Jones       return NextT::equals(lhs + Bytes, rhs + Bytes, size - Bytes);
584cd2f5d5bSGuillaume Chatelet     }
585cd2f5d5bSGuillaume Chatelet 
5861c92911eSMichael Jones     static bool equals(const char *lhs, const char *rhs) {
5871c92911eSMichael Jones       return NextT::equals(lhs + Bytes, rhs + Bytes);
588cd2f5d5bSGuillaume Chatelet     }
589cd2f5d5bSGuillaume Chatelet 
5901c92911eSMichael Jones     static int three_way_compare(const char *lhs, const char *rhs,
5911c92911eSMichael Jones                                  size_t size) {
5921c92911eSMichael Jones       return NextT::three_way_compare(lhs + Bytes, rhs + Bytes, size - Bytes);
593cd2f5d5bSGuillaume Chatelet     }
594cd2f5d5bSGuillaume Chatelet 
5951c92911eSMichael Jones     static int three_way_compare(const char *lhs, const char *rhs) {
5961c92911eSMichael Jones       return NextT::three_way_compare(lhs + Bytes, rhs + Bytes);
597cd2f5d5bSGuillaume Chatelet     }
598cd2f5d5bSGuillaume Chatelet 
5991c92911eSMichael Jones     static void splat_set(char *dst, const unsigned char value, size_t size) {
6001c92911eSMichael Jones       NextT::splat_set(dst + Bytes, value, size - Bytes);
601cd2f5d5bSGuillaume Chatelet     }
602cd2f5d5bSGuillaume Chatelet 
6031c92911eSMichael Jones     static void splat_set(char *dst, const unsigned char value) {
6041c92911eSMichael Jones       NextT::splat_set(dst + Bytes, value);
605cd2f5d5bSGuillaume Chatelet     }
606cd2f5d5bSGuillaume Chatelet   };
607cd2f5d5bSGuillaume Chatelet };
608cd2f5d5bSGuillaume Chatelet 
6097fff39d9SGuillaume Chatelet // Fixed-size Builtin Operations
6107fff39d9SGuillaume Chatelet // -----------------------------
6117fff39d9SGuillaume Chatelet // Note: Do not use 'builtin' right now as it requires the implementation of the
6127fff39d9SGuillaume Chatelet // `_inline` versions of all the builtins. Theoretically, Clang can still turn
6137fff39d9SGuillaume Chatelet // them into calls to the C library leading to reentrancy problems.
6147fff39d9SGuillaume Chatelet namespace builtin {
6157fff39d9SGuillaume Chatelet 
6167fff39d9SGuillaume Chatelet #ifndef __has_builtin
6177fff39d9SGuillaume Chatelet #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
6187fff39d9SGuillaume Chatelet #endif
6197fff39d9SGuillaume Chatelet 
6207fff39d9SGuillaume Chatelet template <size_t Size> struct Builtin {
6211c92911eSMichael Jones   static constexpr size_t SIZE = Size;
6227fff39d9SGuillaume Chatelet 
6231c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {
6247fff39d9SGuillaume Chatelet #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER
6255b3a51fdSMichael Jones     for_loop_copy(dst, src);
6267fff39d9SGuillaume Chatelet #elif __has_builtin(__builtin_memcpy_inline)
6277fff39d9SGuillaume Chatelet     // __builtin_memcpy_inline guarantees to never call external functions.
6287fff39d9SGuillaume Chatelet     // Unfortunately it is not widely available.
6291c92911eSMichael Jones     __builtin_memcpy_inline(dst, src, SIZE);
6307fff39d9SGuillaume Chatelet #else
6315b3a51fdSMichael Jones     for_loop_copy(dst, src);
6327fff39d9SGuillaume Chatelet #endif
6337fff39d9SGuillaume Chatelet   }
6347fff39d9SGuillaume Chatelet 
6351c92911eSMichael Jones   static void move(char *dst, const char *src) {
6361b927b68SGuillaume Chatelet #if LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER
6371c92911eSMichael Jones     for_loop_move(dst, src);
6381b927b68SGuillaume Chatelet #elif __has_builtin(__builtin_memmove)
6391c92911eSMichael Jones     __builtin_memmove(dst, src, SIZE);
6401b927b68SGuillaume Chatelet #else
6411c92911eSMichael Jones     for_loop_move(dst, src);
6421b927b68SGuillaume Chatelet #endif
6431b927b68SGuillaume Chatelet   }
6441b927b68SGuillaume Chatelet 
645366805eaSAndre Vieira #if __has_builtin(__builtin_memcmp_inline)
646366805eaSAndre Vieira #define LLVM_LIBC_MEMCMP __builtin_memcmp_inline
647366805eaSAndre Vieira #else
648366805eaSAndre Vieira #define LLVM_LIBC_MEMCMP __builtin_memcmp
649366805eaSAndre Vieira #endif
650366805eaSAndre Vieira 
6511c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) {
6521c92911eSMichael Jones     return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE) == 0;
6537fff39d9SGuillaume Chatelet   }
6547fff39d9SGuillaume Chatelet 
6551c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) {
6561c92911eSMichael Jones     return LLVM_LIBC_MEMCMP(lhs, rhs, SIZE);
6577fff39d9SGuillaume Chatelet   }
6587fff39d9SGuillaume Chatelet 
6591c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {
6601c92911eSMichael Jones     __builtin_memset(dst, value, SIZE);
6617fff39d9SGuillaume Chatelet   }
6627fff39d9SGuillaume Chatelet 
6637fff39d9SGuillaume Chatelet private:
6641c92911eSMichael Jones   // Copies `SIZE` bytes from `src` to `dst` using a for loop.
6657abd8f6cSCheng Wang   // This code requires the use of `-fno-builtin-memcpy` to prevent the compiler
6667fff39d9SGuillaume Chatelet   // from turning the for-loop back into `__builtin_memcpy`.
6671c92911eSMichael Jones   static void for_loop_copy(char *__restrict dst, const char *__restrict src) {
6681c92911eSMichael Jones     for (size_t i = 0; i < SIZE; ++i)
6697fff39d9SGuillaume Chatelet       dst[i] = src[i];
6707fff39d9SGuillaume Chatelet   }
6711b927b68SGuillaume Chatelet 
6721c92911eSMichael Jones   static void for_loop_move(char *dst, const char *src) {
6731c92911eSMichael Jones     for (size_t i = 0; i < SIZE; ++i)
6741b927b68SGuillaume Chatelet       dst[i] = src[i];
6751b927b68SGuillaume Chatelet   }
6767fff39d9SGuillaume Chatelet };
6777fff39d9SGuillaume Chatelet 
6787fff39d9SGuillaume Chatelet using _1 = Builtin<1>;
6797fff39d9SGuillaume Chatelet using _2 = Builtin<2>;
6807fff39d9SGuillaume Chatelet using _3 = Builtin<3>;
6817fff39d9SGuillaume Chatelet using _4 = Builtin<4>;
6827fff39d9SGuillaume Chatelet using _8 = Builtin<8>;
6837fff39d9SGuillaume Chatelet using _16 = Builtin<16>;
6847fff39d9SGuillaume Chatelet using _32 = Builtin<32>;
6857fff39d9SGuillaume Chatelet using _64 = Builtin<64>;
6867fff39d9SGuillaume Chatelet using _128 = Builtin<128>;
6877fff39d9SGuillaume Chatelet 
6887fff39d9SGuillaume Chatelet } // namespace builtin
6897fff39d9SGuillaume Chatelet 
6907fff39d9SGuillaume Chatelet // Fixed-size Scalar Operations
6917fff39d9SGuillaume Chatelet // ----------------------------
6927fff39d9SGuillaume Chatelet namespace scalar {
6937fff39d9SGuillaume Chatelet 
6947fff39d9SGuillaume Chatelet // The Scalar type makes use of simple sized integers.
6957fff39d9SGuillaume Chatelet template <typename T> struct Scalar {
6961c92911eSMichael Jones   static constexpr size_t SIZE = sizeof(T);
6977fff39d9SGuillaume Chatelet 
6981c92911eSMichael Jones   static void copy(char *__restrict dst, const char *__restrict src) {
6991c92911eSMichael Jones     store(dst, load(src));
7007fff39d9SGuillaume Chatelet   }
7017fff39d9SGuillaume Chatelet 
7021c92911eSMichael Jones   static void move(char *dst, const char *src) { store(dst, load(src)); }
7031b927b68SGuillaume Chatelet 
7041c92911eSMichael Jones   static bool equals(const char *lhs, const char *rhs) {
7051c92911eSMichael Jones     return load(lhs) == load(rhs);
7067fff39d9SGuillaume Chatelet   }
7077fff39d9SGuillaume Chatelet 
7081c92911eSMichael Jones   static int three_way_compare(const char *lhs, const char *rhs) {
7091c92911eSMichael Jones     return scalar_three_way_compare(load(lhs), load(rhs));
7107fff39d9SGuillaume Chatelet   }
7117fff39d9SGuillaume Chatelet 
7121c92911eSMichael Jones   static void splat_set(char *dst, const unsigned char value) {
7131c92911eSMichael Jones     store(dst, get_splatted_value(value));
7147fff39d9SGuillaume Chatelet   }
7157fff39d9SGuillaume Chatelet 
7161c92911eSMichael Jones   static int scalar_three_way_compare(T a, T b);
717366805eaSAndre Vieira 
7181c92911eSMichael Jones   static T load(const char *ptr) {
7197fff39d9SGuillaume Chatelet     T value;
7201c92911eSMichael Jones     builtin::Builtin<SIZE>::copy(reinterpret_cast<char *>(&value), ptr);
7217fff39d9SGuillaume Chatelet     return value;
7227fff39d9SGuillaume Chatelet   }
7231c92911eSMichael Jones   static void store(char *ptr, T value) {
7241c92911eSMichael Jones     builtin::Builtin<SIZE>::copy(ptr, reinterpret_cast<const char *>(&value));
7257fff39d9SGuillaume Chatelet   }
7261b927b68SGuillaume Chatelet 
7271b927b68SGuillaume Chatelet private:
7281c92911eSMichael Jones   static T get_splatted_value(const unsigned char value) {
7297fff39d9SGuillaume Chatelet     return T(~0) / T(0xFF) * T(value);
7307fff39d9SGuillaume Chatelet   }
7317fff39d9SGuillaume Chatelet };
7327fff39d9SGuillaume Chatelet 
7337fff39d9SGuillaume Chatelet template <>
7341c92911eSMichael Jones inline int Scalar<uint8_t>::scalar_three_way_compare(uint8_t a, uint8_t b) {
7351c92911eSMichael Jones   const int16_t la = Endian::to_big_endian(a);
7361c92911eSMichael Jones   const int16_t lb = Endian::to_big_endian(b);
7377fff39d9SGuillaume Chatelet   return la - lb;
7387fff39d9SGuillaume Chatelet }
7397fff39d9SGuillaume Chatelet template <>
7401c92911eSMichael Jones inline int Scalar<uint16_t>::scalar_three_way_compare(uint16_t a, uint16_t b) {
7411c92911eSMichael Jones   const int32_t la = Endian::to_big_endian(a);
7421c92911eSMichael Jones   const int32_t lb = Endian::to_big_endian(b);
7437fff39d9SGuillaume Chatelet   return la - lb;
7447fff39d9SGuillaume Chatelet }
7457fff39d9SGuillaume Chatelet template <>
7461c92911eSMichael Jones inline int Scalar<uint32_t>::scalar_three_way_compare(uint32_t a, uint32_t b) {
7471c92911eSMichael Jones   const uint32_t la = Endian::to_big_endian(a);
7481c92911eSMichael Jones   const uint32_t lb = Endian::to_big_endian(b);
749366805eaSAndre Vieira   return la > lb ? 1 : la < lb ? -1 : 0;
7507fff39d9SGuillaume Chatelet }
7517fff39d9SGuillaume Chatelet template <>
7521c92911eSMichael Jones inline int Scalar<uint64_t>::scalar_three_way_compare(uint64_t a, uint64_t b) {
7531c92911eSMichael Jones   const uint64_t la = Endian::to_big_endian(a);
7541c92911eSMichael Jones   const uint64_t lb = Endian::to_big_endian(b);
755366805eaSAndre Vieira   return la > lb ? 1 : la < lb ? -1 : 0;
7567fff39d9SGuillaume Chatelet }
7577fff39d9SGuillaume Chatelet 
7587fff39d9SGuillaume Chatelet using UINT8 = Scalar<uint8_t>;   // 1 Byte
7597fff39d9SGuillaume Chatelet using UINT16 = Scalar<uint16_t>; // 2 Bytes
7607fff39d9SGuillaume Chatelet using UINT32 = Scalar<uint32_t>; // 4 Bytes
7617fff39d9SGuillaume Chatelet using UINT64 = Scalar<uint64_t>; // 8 Bytes
7627fff39d9SGuillaume Chatelet 
7637fff39d9SGuillaume Chatelet using _1 = UINT8;
7647fff39d9SGuillaume Chatelet using _2 = UINT16;
7657fff39d9SGuillaume Chatelet using _3 = Chained<UINT16, UINT8>;
7667fff39d9SGuillaume Chatelet using _4 = UINT32;
7677fff39d9SGuillaume Chatelet using _8 = UINT64;
7687fff39d9SGuillaume Chatelet using _16 = Repeated<_8, 2>;
7697fff39d9SGuillaume Chatelet using _32 = Repeated<_8, 4>;
7707fff39d9SGuillaume Chatelet using _64 = Repeated<_8, 8>;
7717fff39d9SGuillaume Chatelet using _128 = Repeated<_8, 16>;
7727fff39d9SGuillaume Chatelet 
7737fff39d9SGuillaume Chatelet } // namespace scalar
7747fff39d9SGuillaume Chatelet } // namespace __llvm_libc
7757fff39d9SGuillaume Chatelet 
776366805eaSAndre Vieira #include <src/string/memory_utils/elements_aarch64.h>
7777fff39d9SGuillaume Chatelet #include <src/string/memory_utils/elements_x86.h>
7787fff39d9SGuillaume Chatelet 
7797fff39d9SGuillaume Chatelet #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_H
780