| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/base/memory.h" | |
| #include <stdint.h> | |
| #include "sw/device/lib/base/macros.h" | |
| #ifdef OT_PLATFORM_RV32 | |
| #define OT_PREFIX_IF_NOT_RV32(name) name | |
| #else | |
| #define OT_PREFIX_IF_NOT_RV32(name) ot_##name | |
| #endif | |
| static size_t compute_num_leading_bytes(const void *left, const void *right, | |
| size_t len) { | |
| if (len < alignof(uint32_t)) { | |
| return len; | |
| } | |
| const size_t left_ahead = misalignment32_of((uintptr_t)left); | |
| const size_t right_ahead = misalignment32_of((uintptr_t)right); | |
| if (right == NULL || left_ahead == right_ahead) { | |
| return (4 - left_ahead) & 0x3; | |
| } | |
| return len; | |
| } | |
| /** | |
| * Compute the bounds of the word-aligned region for the given buffers. | |
| * | |
| * It's more efficient for our memory functions to operate on `uint32_t` values | |
| * than individual bytes, but we can only read `uint32_t` values from aligned | |
| * addresses. This function effectively breaks the given buffers into three | |
| * consecutive chunks: the unaligned "head", the aligned "body", and the | |
| * unaligned "tail". | |
| * | |
| * @param[in] left The memory function's first buffer argument. Cannot be NULL. | |
| * @param[in] right The memory function's second buffer argument. May be NULL. | |
| * @param[in] len The length in bytes of both `left` and `right.` | |
| * @param[out] out_body_offset The start of the body region. | |
| * @param[out] out_tail_offset The start of the tail region. | |
| */ | |
| static void compute_alignment(const void *left, const void *right, size_t len, | |
| size_t *out_body_offset, | |
| size_t *out_tail_offset) { | |
| const size_t num_leading_bytes = compute_num_leading_bytes(left, right, len); | |
| *out_body_offset = num_leading_bytes; | |
| const size_t num_words = (len - num_leading_bytes) / sizeof(uint32_t); | |
| *out_tail_offset = num_leading_bytes + num_words * sizeof(uint32_t); | |
| } | |
| static uint32_t repeat_byte_to_u32(uint8_t byte) { | |
| return byte << 24 | byte << 16 | byte << 8 | byte; | |
| } | |
| void *OT_PREFIX_IF_NOT_RV32(memcpy)(void *restrict dest, | |
| const void *restrict src, size_t len) { | |
| if (dest == NULL || src == NULL) { | |
| return dest; | |
| } | |
| unsigned char *dest8 = (unsigned char *)dest; | |
| const unsigned char *src8 = (const unsigned char *)src; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(dest, src, len, &body_offset, &tail_offset); | |
| size_t i = 0; | |
| for (; i < body_offset; ++i) { | |
| dest8[i] = src8[i]; | |
| } | |
| for (; i < tail_offset; i += sizeof(uint32_t)) { | |
| uint32_t word = read_32(&src8[i]); | |
| write_32(word, &dest8[i]); | |
| } | |
| for (; i < len; ++i) { | |
| dest8[i] = src8[i]; | |
| } | |
| return dest; | |
| } | |
| void *OT_PREFIX_IF_NOT_RV32(memset)(void *dest, int value, size_t len) { | |
| unsigned char *dest8 = (unsigned char *)dest; | |
| const uint8_t value8 = (uint8_t)value; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(dest, NULL, len, &body_offset, &tail_offset); | |
| size_t i = 0; | |
| for (; i < body_offset; ++i) { | |
| dest8[i] = value8; | |
| } | |
| const uint32_t value32 = repeat_byte_to_u32(value8); | |
| for (; i < tail_offset; i += sizeof(uint32_t)) { | |
| write_32(value32, &dest8[i]); | |
| } | |
| for (; i < len; ++i) { | |
| dest8[i] = value8; | |
| } | |
| return dest; | |
| } | |
| enum { | |
| kMemCmpEq = 0, | |
| kMemCmpLt = -42, | |
| kMemCmpGt = 42, | |
| }; | |
| int OT_PREFIX_IF_NOT_RV32(memcmp)(const void *lhs, const void *rhs, | |
| size_t len) { | |
| const unsigned char *lhs8 = (const unsigned char *)lhs; | |
| const unsigned char *rhs8 = (const unsigned char *)rhs; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(lhs, rhs, len, &body_offset, &tail_offset); | |
| size_t i = 0; | |
| for (; i < body_offset; ++i) { | |
| if (lhs8[i] < rhs8[i]) { | |
| return kMemCmpLt; | |
| } else if (lhs8[i] > rhs8[i]) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| for (; i < tail_offset; i += sizeof(uint32_t)) { | |
| uint32_t word_left = __builtin_bswap32(read_32(&lhs8[i])); | |
| uint32_t word_right = __builtin_bswap32(read_32(&rhs8[i])); | |
| static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, | |
| "memcmp assumes that the system is little endian."); | |
| if (word_left < word_right) { | |
| return kMemCmpLt; | |
| } else if (word_left > word_right) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| for (; i < len; ++i) { | |
| if (lhs8[i] < rhs8[i]) { | |
| return kMemCmpLt; | |
| } else if (lhs8[i] > rhs8[i]) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| return kMemCmpEq; | |
| } | |
| int memrcmp(const void *lhs, const void *rhs, size_t len) { | |
| const unsigned char *lhs8 = (const unsigned char *)lhs; | |
| const unsigned char *rhs8 = (const unsigned char *)rhs; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(lhs, rhs, len, &body_offset, &tail_offset); | |
| size_t end = len; | |
| for (; end > tail_offset; --end) { | |
| const size_t i = end - 1; | |
| if (lhs8[i] < rhs8[i]) { | |
| return kMemCmpLt; | |
| } else if (lhs8[i] > rhs8[i]) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| for (; end > body_offset; end -= sizeof(uint32_t)) { | |
| const size_t i = end - sizeof(uint32_t); | |
| uint32_t word_left = read_32(&lhs8[i]); | |
| uint32_t word_right = read_32(&rhs8[i]); | |
| static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, | |
| "memrcmp assumes that the system is little endian."); | |
| if (word_left < word_right) { | |
| return kMemCmpLt; | |
| } else if (word_left > word_right) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| for (; end > 0; --end) { | |
| const size_t i = end - 1; | |
| if (lhs8[i] < rhs8[i]) { | |
| return kMemCmpLt; | |
| } else if (lhs8[i] > rhs8[i]) { | |
| return kMemCmpGt; | |
| } | |
| } | |
| return kMemCmpEq; | |
| } | |
| void *OT_PREFIX_IF_NOT_RV32(memchr)(const void *ptr, int value, size_t len) { | |
| const unsigned char *ptr8 = (const unsigned char *)ptr; | |
| const uint8_t value8 = (uint8_t)value; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(ptr, NULL, len, &body_offset, &tail_offset); | |
| size_t i = 0; | |
| for (; i < body_offset; ++i) { | |
| if (ptr8[i] == value8) { | |
| return (void *)&ptr8[i]; | |
| } | |
| } | |
| const uint32_t value32 = repeat_byte_to_u32(value8); | |
| for (; i < tail_offset; i += sizeof(uint32_t)) { | |
| uint32_t word = read_32(&ptr8[i]); | |
| uint32_t bits_eq = ~(word ^ value32); | |
| static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, | |
| "memchr assumes that the system is little endian."); | |
| if ((bits_eq & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i]; | |
| } | |
| if (((bits_eq >> 8) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 1]; | |
| } | |
| if (((bits_eq >> 16) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 2]; | |
| } | |
| if (((bits_eq >> 24) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 3]; | |
| } | |
| } | |
| for (; i < len; ++i) { | |
| if (ptr8[i] == value8) { | |
| return (void *)&ptr8[i]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| void *OT_PREFIX_IF_NOT_RV32(memrchr)(const void *ptr, int value, size_t len) { | |
| const unsigned char *ptr8 = (const unsigned char *)ptr; | |
| const uint8_t value8 = (uint8_t)value; | |
| size_t body_offset, tail_offset; | |
| compute_alignment(ptr, NULL, len, &body_offset, &tail_offset); | |
| size_t end = len; | |
| for (; end > tail_offset; --end) { | |
| const size_t i = end - 1; | |
| if (ptr8[i] == value8) { | |
| return (void *)&ptr8[i]; | |
| } | |
| } | |
| const uint32_t value32 = repeat_byte_to_u32(value8); | |
| for (; end > body_offset; end -= sizeof(uint32_t)) { | |
| const size_t i = end - sizeof(uint32_t); | |
| uint32_t word = read_32(&ptr8[i]); | |
| uint32_t bits_eq = ~(word ^ value32); | |
| static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, | |
| "memrchr assumes that the system is little endian."); | |
| if (((bits_eq >> 24) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 3]; | |
| } | |
| if (((bits_eq >> 16) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 2]; | |
| } | |
| if (((bits_eq >> 8) & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i + 1]; | |
| } | |
| if ((bits_eq & UINT8_MAX) == UINT8_MAX) { | |
| return (void *)&ptr8[i]; | |
| } | |
| } | |
| for (; end > 0; --end) { | |
| const size_t i = end - 1; | |
| if (ptr8[i] == value8) { | |
| return (void *)&ptr8[i]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| // `extern` declarations to give the inline functions in the corresponding | |
| // header a link location. | |
| extern ptrdiff_t misalignment32_of(uintptr_t); | |
| extern uint32_t read_32(const void *); | |
| extern void write_32(uint32_t, void *); | |
| extern uint64_t read_64(const void *); | |
| extern void write_64(uint64_t, void *); | |
| #undef OT_PREFIX_IF_NOT_RV32 |