#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ARM_INLINE_MEMSET_H
#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ARM_INLINE_MEMSET_H
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/optimization.h"
#include "src/string/memory_utils/arm/common.h"
#include "src/string/memory_utils/utils.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace {
template <size_t bytes, AssumeAccess access>
LIBC_INLINE void set(void *dst, uint32_t value) {
static_assert(bytes == 1 || bytes == 2 || bytes == 4);
if constexpr (access == AssumeAccess::kAligned) {
constexpr size_t alignment = bytes > kWordSize ? kWordSize : bytes;
memcpy_inline<bytes>(assume_aligned<alignment>(dst), &value);
} else if constexpr (access == AssumeAccess::kUnknown) {
memcpy_inline<bytes>(dst, &value);
} else {
static_assert(cpp::always_false<decltype(access)>, "Invalid AssumeAccess");
}
}
template <size_t bytes, AssumeAccess access = AssumeAccess::kUnknown>
LIBC_INLINE void set_block_and_bump_pointers(Ptr &dst, uint32_t value) {
if constexpr (bytes <= kWordSize) {
set<bytes, access>(dst, value);
} else {
static_assert(bytes % kWordSize == 0 && bytes >= kWordSize);
LIBC_LOOP_UNROLL
for (size_t offset = 0; offset < bytes; offset += kWordSize) {
set<kWordSize, access>(dst + offset, value);
}
}
dst += bytes;
}
template <size_t bytes, AssumeAccess access>
LIBC_INLINE void consume_by_block(Ptr &dst, uint32_t value, size_t &size) {
LIBC_LOOP_NOUNROLL
for (size_t i = 0; i < size / bytes; ++i)
set_block_and_bump_pointers<bytes, access>(dst, value);
size %= bytes;
}
[[maybe_unused]] LIBC_INLINE void
set_bytes_and_bump_pointers(Ptr &dst, uint32_t value, size_t size) {
LIBC_LOOP_NOUNROLL
for (size_t i = 0; i < size; ++i) {
set<1, AssumeAccess::kUnknown>(dst++, value);
}
}
}
[[maybe_unused]] LIBC_INLINE void
inline_memset_arm_low_end(Ptr dst, uint8_t value, size_t size) {
if (size >= 8)
LIBC_ATTR_LIKELY {
if (const size_t offset = distance_to_align_up<kWordSize>(dst))
LIBC_ATTR_UNLIKELY {
set_bytes_and_bump_pointers(dst, value, offset);
size -= offset;
}
const uint32_t value32 = value * 0x01010101U;
consume_by_block<64, AssumeAccess::kAligned>(dst, value32, size);
consume_by_block<16, AssumeAccess::kAligned>(dst, value32, size);
consume_by_block<4, AssumeAccess::kAligned>(dst, value32, size);
}
set_bytes_and_bump_pointers(dst, value, size);
}
[[maybe_unused]] LIBC_INLINE void
inline_memset_arm_mid_end(Ptr dst, uint8_t value, size_t size) {
const uint32_t value32 = value * 0x01010101U;
if (misaligned(dst))
LIBC_ATTR_UNLIKELY {
if (size < 8)
LIBC_ATTR_UNLIKELY {
if (size & 1)
set_block_and_bump_pointers<1>(dst, value32);
if (size & 2)
set_block_and_bump_pointers<2>(dst, value32);
if (size & 4)
set_block_and_bump_pointers<4>(dst, value32);
return;
}
const size_t offset = distance_to_align_up<kWordSize>(dst);
if (offset & 1)
set_block_and_bump_pointers<1>(dst, value32);
if (offset & 2)
set_block_and_bump_pointers<2>(dst, value32);
size -= offset;
}
consume_by_block<64, AssumeAccess::kUnknown>(dst, value32, size);
consume_by_block<16, AssumeAccess::kAligned>(dst, value32, size);
consume_by_block<4, AssumeAccess::kAligned>(dst, value32, size);
if (size & 1)
set_block_and_bump_pointers<1>(dst, value32);
if (size & 2)
LIBC_ATTR_UNLIKELY
set_block_and_bump_pointers<2>(dst, value32);
}
[[maybe_unused]] LIBC_INLINE void
inline_memset_arm_dispatch(Ptr dst, uint8_t value, size_t size) {
#ifdef __ARM_FEATURE_UNALIGNED
return inline_memset_arm_mid_end(dst, value, size);
#else
return inline_memset_arm_low_end(dst, value, size);
#endif
}
}
#endif