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