1 //===-- Memory utils --------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_SRC_MEMORY_UTILS_UTILS_H
10 #define LLVM_LIBC_SRC_MEMORY_UTILS_UTILS_H
11 
12 #include "src/__support/architectures.h"
13 
14 // Cache line sizes for ARM: These values are not strictly correct since
15 // cache line sizes depend on implementations, not architectures.  There
16 // are even implementations with cache line sizes configurable at boot
17 // time.
18 #if defined(LLVM_LIBC_ARCH_AARCH64) || defined(LLVM_LIBC_ARCH_X86)
19 #define LLVM_LIBC_CACHELINE_SIZE 64
20 #else
21 #error "Unsupported platform for memory functions."
22 #endif
23 
24 #include <stddef.h> // size_t
25 #include <stdint.h> // intptr_t / uintptr_t
26 
27 namespace __llvm_libc {
28 
29 // Return whether `value` is zero or a power of two.
30 static constexpr bool is_power2_or_zero(size_t value) {
31   return (value & (value - 1U)) == 0;
32 }
33 
34 // Return whether `value` is a power of two.
35 static constexpr bool is_power2(size_t value) {
36   return value && is_power2_or_zero(value);
37 }
38 
39 // Compile time version of log2 that handles 0.
40 static constexpr size_t log2(size_t value) {
41   return (value == 0 || value == 1) ? 0 : 1 + log2(value / 2);
42 }
43 
44 // Returns the first power of two preceding value or value if it is already a
45 // power of two (or 0 when value is 0).
46 static constexpr size_t le_power2(size_t value) {
47   return value == 0 ? value : 1ULL << log2(value);
48 }
49 
50 // Returns the first power of two following value or value if it is already a
51 // power of two (or 0 when value is 0).
52 static constexpr size_t ge_power2(size_t value) {
53   return is_power2_or_zero(value) ? value : 1ULL << (log2(value) + 1);
54 }
55 
56 template <size_t alignment> intptr_t offset_from_last_aligned(const void *ptr) {
57   static_assert(is_power2(alignment), "alignment must be a power of 2");
58   return reinterpret_cast<uintptr_t>(ptr) & (alignment - 1U);
59 }
60 
61 template <size_t alignment> intptr_t offset_to_next_aligned(const void *ptr) {
62   static_assert(is_power2(alignment), "alignment must be a power of 2");
63   // The logic is not straightforward and involves unsigned modulo arithmetic
64   // but the generated code is as fast as it can be.
65   return -reinterpret_cast<uintptr_t>(ptr) & (alignment - 1U);
66 }
67 
68 // Returns the offset from `ptr` to the next cache line.
69 static inline intptr_t offset_to_next_cache_line(const void *ptr) {
70   return offset_to_next_aligned<LLVM_LIBC_CACHELINE_SIZE>(ptr);
71 }
72 
73 template <size_t alignment, typename T> static T *assume_aligned(T *ptr) {
74   return reinterpret_cast<T *>(__builtin_assume_aligned(ptr, alignment));
75 }
76 
77 } // namespace __llvm_libc
78 
79 #endif // LLVM_LIBC_SRC_MEMORY_UTILS_UTILS_H
80