//===-- Strongly typed address with alignment and access semantics --------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // //===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H #include "src/__support/CPP/TypeTraits.h" // cpp::ConditionalType #include "src/string/memory_utils/utils.h" // is_power2 #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t namespace __llvm_libc { // Utility to enable static_assert(false) in templates. template static void DeferredStaticAssert(const char *msg) { static_assert(flag, "compilation error"); } // A non-coercible type to represent raw data. enum class ubyte : unsigned char { ZERO = 0 }; // Address attribute specifying whether the underlying load / store operations // are temporal or non-temporal. enum class Temporality { TEMPORAL, NON_TEMPORAL }; // Address attribute specifying whether the underlying load / store operations // are aligned or unaligned. enum class Aligned { NO, YES }; // Address attribute to discriminate between readable and writable addresses. enum class Permission { Read, Write }; // Address is semantically equivalent to a pointer but also conveys compile time // information that helps with instructions selection (aligned/unaligned, // temporal/non-temporal). template struct Address { static_assert(is_power2(Alignment)); static constexpr size_t ALIGNMENT = Alignment; static constexpr Permission PERMISSION = P; static constexpr Temporality TEMPORALITY = TS; static constexpr bool IS_READ = P == Permission::Read; static constexpr bool IS_WRITE = P == Permission::Write; using PointeeType = cpp::ConditionalType; using VoidType = cpp::ConditionalType; Address(VoidType *ptr) : ptr_(reinterpret_cast(ptr)) {} PointeeType *ptr() const { return reinterpret_cast( __builtin_assume_aligned(ptr_, ALIGNMENT)); } PointeeType *const ptr_; template auto offset(size_t byte_offset) const { static constexpr size_t NewAlignment = commonAlign(); return Address(ptr_ + byte_offset); } private: static constexpr size_t gcd(size_t A, size_t B) { return B == 0 ? A : gcd(B, A % B); } template static constexpr size_t commonAlign() { constexpr size_t GCD = gcd(ByteOffset, ALIGNMENT); if constexpr (is_power2(GCD)) return GCD; else return 1; } }; template struct IsAddressType : public cpp::FalseValue {}; template struct IsAddressType> : public cpp::TrueValue {}; // Reinterpret the address as a pointer to T. // This is not UB since the underlying pointer always refers to a `char` in a // buffer of raw data. template static T *as(AddrT addr) { static_assert(IsAddressType::Value); return reinterpret_cast(addr.ptr()); } // Offsets the address by a compile time amount, this allows propagating // alignment whenever possible. template static auto offsetAddr(AddrT addr) { static_assert(IsAddressType::Value); return addr.template offset(ByteOffset); } // Offsets the address by a runtime amount but assuming that the resulting // address will be Alignment aligned. template static auto offsetAddrAssumeAligned(AddrT addr, size_t byte_offset) { static_assert(IsAddressType::Value); return Address(addr.ptr_ + byte_offset); } // Offsets the address by a runtime amount that is assumed to be a multiple of // ByteOffset. This allows to propagate the address alignment whenever possible. template static auto offsetAddrMultiplesOf(AddrT addr, ptrdiff_t byte_offset) { static_assert(IsAddressType::Value); return addr.template offset(byte_offset); } // User friendly aliases for common address types. template using SrcAddr = Address; template using DstAddr = Address; template using NtSrcAddr = Address; template using NtDstAddr = Address; } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H