1 //===-- Strongly typed address with alignment and access semantics --------===//
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 //
10 //===----------------------------------------------------------------------===//
11
12 #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H
13 #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H
14
15 #include "src/__support/CPP/TypeTraits.h" // cpp::ConditionalType
16 #include "src/string/memory_utils/utils.h" // is_power2
17 #include <stddef.h> // size_t
18 #include <stdint.h> // uint8_t, uint16_t, uint32_t, uint64_t
19
20 namespace __llvm_libc {
21
22 // Utility to enable static_assert(false) in templates.
DeferredStaticAssert(const char * msg)23 template <bool flag = false> static void DeferredStaticAssert(const char *msg) {
24 static_assert(flag, "compilation error");
25 }
26
27 // A non-coercible type to represent raw data.
28 enum class ubyte : unsigned char { ZERO = 0 };
29
30 // Address attribute specifying whether the underlying load / store operations
31 // are temporal or non-temporal.
32 enum class Temporality { TEMPORAL, NON_TEMPORAL };
33
34 // Address attribute specifying whether the underlying load / store operations
35 // are aligned or unaligned.
36 enum class Aligned { NO, YES };
37
38 // Address attribute to discriminate between readable and writable addresses.
39 enum class Permission { Read, Write };
40
41 // Address is semantically equivalent to a pointer but also conveys compile time
42 // information that helps with instructions selection (aligned/unaligned,
43 // temporal/non-temporal).
44 template <size_t Alignment, Permission P, Temporality TS> struct Address {
45 static_assert(is_power2(Alignment));
46 static constexpr size_t ALIGNMENT = Alignment;
47 static constexpr Permission PERMISSION = P;
48 static constexpr Temporality TEMPORALITY = TS;
49 static constexpr bool IS_READ = P == Permission::Read;
50 static constexpr bool IS_WRITE = P == Permission::Write;
51 using PointeeType = cpp::ConditionalType<!IS_WRITE, const ubyte, ubyte>;
52 using VoidType = cpp::ConditionalType<!IS_WRITE, const void, void>;
53
AddressAddress54 Address(VoidType *ptr) : ptr_(reinterpret_cast<PointeeType *>(ptr)) {}
55
ptrAddress56 PointeeType *ptr() const {
57 return reinterpret_cast<PointeeType *>(
58 __builtin_assume_aligned(ptr_, ALIGNMENT));
59 }
60
61 PointeeType *const ptr_;
62
offsetAddress63 template <size_t ByteOffset> auto offset(size_t byte_offset) const {
64 static constexpr size_t NewAlignment = commonAlign<ByteOffset>();
65 return Address<NewAlignment, PERMISSION, TEMPORALITY>(ptr_ + byte_offset);
66 }
67
68 private:
gcdAddress69 static constexpr size_t gcd(size_t A, size_t B) {
70 return B == 0 ? A : gcd(B, A % B);
71 }
72
commonAlignAddress73 template <size_t ByteOffset> static constexpr size_t commonAlign() {
74 constexpr size_t GCD = gcd(ByteOffset, ALIGNMENT);
75 if constexpr (is_power2(GCD))
76 return GCD;
77 else
78 return 1;
79 }
80 };
81
82 template <typename T> struct IsAddressType : public cpp::FalseValue {};
83 template <size_t Alignment, Permission P, Temporality TS>
84 struct IsAddressType<Address<Alignment, P, TS>> : public cpp::TrueValue {};
85
86 // Reinterpret the address as a pointer to T.
87 // This is not UB since the underlying pointer always refers to a `char` in a
88 // buffer of raw data.
89 template <typename T, typename AddrT> static T *as(AddrT addr) {
90 static_assert(IsAddressType<AddrT>::Value);
91 return reinterpret_cast<T *>(addr.ptr());
92 }
93
94 // Offsets the address by a compile time amount, this allows propagating
95 // alignment whenever possible.
96 template <size_t ByteOffset, typename AddrT>
97 static auto offsetAddr(AddrT addr) {
98 static_assert(IsAddressType<AddrT>::Value);
99 return addr.template offset<ByteOffset>(ByteOffset);
100 }
101
102 // Offsets the address by a runtime amount but assuming that the resulting
103 // address will be Alignment aligned.
104 template <size_t Alignment, typename AddrT>
105 static auto offsetAddrAssumeAligned(AddrT addr, size_t byte_offset) {
106 static_assert(IsAddressType<AddrT>::Value);
107 return Address<Alignment, AddrT::PERMISSION, AddrT::TEMPORALITY>(addr.ptr_ +
108 byte_offset);
109 }
110
111 // Offsets the address by a runtime amount that is assumed to be a multiple of
112 // ByteOffset. This allows to propagate the address alignment whenever possible.
113 template <size_t ByteOffset, typename AddrT>
114 static auto offsetAddrMultiplesOf(AddrT addr, ptrdiff_t byte_offset) {
115 static_assert(IsAddressType<AddrT>::Value);
116 return addr.template offset<ByteOffset>(byte_offset);
117 }
118
119 // User friendly aliases for common address types.
120 template <size_t Alignment>
121 using SrcAddr = Address<Alignment, Permission::Read, Temporality::TEMPORAL>;
122 template <size_t Alignment>
123 using DstAddr = Address<Alignment, Permission::Write, Temporality::TEMPORAL>;
124 template <size_t Alignment>
125 using NtSrcAddr =
126 Address<Alignment, Permission::Read, Temporality::NON_TEMPORAL>;
127 template <size_t Alignment>
128 using NtDstAddr =
129 Address<Alignment, Permission::Write, Temporality::NON_TEMPORAL>;
130
131 } // namespace __llvm_libc
132
133 #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H
134