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