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. 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 54 Address(VoidType *ptr) : ptr_(reinterpret_cast<PointeeType *>(ptr)) {} 55 56 PointeeType *ptr() const { 57 return reinterpret_cast<PointeeType *>( 58 __builtin_assume_aligned(ptr_, ALIGNMENT)); 59 } 60 61 PointeeType *const ptr_; 62 63 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: 69 static constexpr size_t gcd(size_t A, size_t B) { 70 return B == 0 ? A : gcd(B, A % B); 71 } 72 73 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