[libc-commits] [libc] 4cbfd2e - [libc][mem*] Address facility + test enum support

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Wed Jun 1 02:10:10 PDT 2022


Author: Guillaume Chatelet
Date: 2022-06-01T09:09:43Z
New Revision: 4cbfd2e7ebb50cb43024a6c37e5e28460e32d1a5

URL: https://github.com/llvm/llvm-project/commit/4cbfd2e7ebb50cb43024a6c37e5e28460e32d1a5
DIFF: https://github.com/llvm/llvm-project/commit/4cbfd2e7ebb50cb43024a6c37e5e28460e32d1a5.diff

LOG: [libc][mem*] Address facility + test enum support

This patch is a subpart of D125768 intented to make the review easier.

The `Address` struct represents a pointer but also adds compile time knowledge
like alignment or temporal/non-temporal that helps with downstream instruction
selection.

Differential Revision: https://reviews.llvm.org/D125966

Added: 
    libc/src/string/memory_utils/address.h
    libc/test/src/string/memory_utils/address_test.cpp

Modified: 
    libc/src/__support/CPP/TypeTraits.h
    libc/test/src/string/memory_utils/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/CPP/TypeTraits.h b/libc/src/__support/CPP/TypeTraits.h
index e498b549e93f4..61f55ff373e35 100644
--- a/libc/src/__support/CPP/TypeTraits.h
+++ b/libc/src/__support/CPP/TypeTraits.h
@@ -85,6 +85,16 @@ template <typename Type> struct IsArithmetic {
       IsIntegral<Type>::Value || IsFloatingPointType<Type>::Value;
 };
 
+// Compile time type selection.
+template <bool _, class TrueT, class FalseT> struct Conditional {
+  using type = TrueT;
+};
+template <class TrueT, class FalseT> struct Conditional<false, TrueT, FalseT> {
+  using type = FalseT;
+};
+template <bool Cond, typename TrueT, typename FalseT>
+using ConditionalType = typename Conditional<Cond, TrueT, FalseT>::type;
+
 } // namespace cpp
 } // namespace __llvm_libc
 

diff  --git a/libc/src/string/memory_utils/address.h b/libc/src/string/memory_utils/address.h
new file mode 100644
index 0000000000000..476c3dd8d5e76
--- /dev/null
+++ b/libc/src/string/memory_utils/address.h
@@ -0,0 +1,133 @@
+//===-- 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 <stddef.h>                        // size_t
+#include <stdint.h> // uint8_t, uint16_t, uint32_t, uint64_t
+
+namespace __llvm_libc {
+
+// Utility to enable static_assert(false) in templates.
+template <bool flag = false> 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 <size_t Alignment, Permission P, Temporality TS> 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<!IS_WRITE, const ubyte, ubyte>;
+  using VoidType = cpp::ConditionalType<!IS_WRITE, const void, void>;
+
+  Address(VoidType *ptr) : ptr_(reinterpret_cast<PointeeType *>(ptr)) {}
+
+  PointeeType *ptr() const {
+    return reinterpret_cast<PointeeType *>(
+        __builtin_assume_aligned(ptr_, ALIGNMENT));
+  }
+
+  PointeeType *const ptr_;
+
+  template <size_t ByteOffset> auto offset(size_t byte_offset) const {
+    static constexpr size_t NewAlignment = commonAlign<ByteOffset>();
+    return Address<NewAlignment, PERMISSION, TEMPORALITY>(ptr_ + byte_offset);
+  }
+
+private:
+  static constexpr size_t gcd(size_t A, size_t B) {
+    return B == 0 ? A : gcd(B, A % B);
+  }
+
+  template <size_t ByteOffset> static constexpr size_t commonAlign() {
+    constexpr size_t GCD = gcd(ByteOffset, ALIGNMENT);
+    if constexpr (is_power2(GCD))
+      return GCD;
+    else
+      return 1;
+  }
+};
+
+template <typename T> struct IsAddressType : public cpp::FalseValue {};
+template <size_t Alignment, Permission P, Temporality TS>
+struct IsAddressType<Address<Alignment, P, TS>> : 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 <typename T, typename AddrT> static T *as(AddrT addr) {
+  static_assert(IsAddressType<AddrT>::Value);
+  return reinterpret_cast<T *>(addr.ptr());
+}
+
+// Offsets the address by a compile time amount, this allows propagating
+// alignment whenever possible.
+template <size_t ByteOffset, typename AddrT>
+static auto offsetAddr(AddrT addr) {
+  static_assert(IsAddressType<AddrT>::Value);
+  return addr.template offset<ByteOffset>(ByteOffset);
+}
+
+// Offsets the address by a runtime amount but assuming that the resulting
+// address will be Alignment aligned.
+template <size_t Alignment, typename AddrT>
+static auto offsetAddrAssumeAligned(AddrT addr, size_t byte_offset) {
+  static_assert(IsAddressType<AddrT>::Value);
+  return Address<Alignment, AddrT::PERMISSION, AddrT::TEMPORALITY>(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 <size_t ByteOffset, typename AddrT>
+static auto offsetAddrMultiplesOf(AddrT addr, ptr
diff _t byte_offset) {
+  static_assert(IsAddressType<AddrT>::Value);
+  return addr.template offset<ByteOffset>(byte_offset);
+}
+
+// User friendly aliases for common address types.
+template <size_t Alignment>
+using SrcAddr = Address<Alignment, Permission::Read, Temporality::TEMPORAL>;
+template <size_t Alignment>
+using DstAddr = Address<Alignment, Permission::Write, Temporality::TEMPORAL>;
+template <size_t Alignment>
+using NtSrcAddr =
+    Address<Alignment, Permission::Read, Temporality::NON_TEMPORAL>;
+template <size_t Alignment>
+using NtDstAddr =
+    Address<Alignment, Permission::Write, Temporality::NON_TEMPORAL>;
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_COMMON_H

diff  --git a/libc/test/src/string/memory_utils/CMakeLists.txt b/libc/test/src/string/memory_utils/CMakeLists.txt
index 7836076121dd9..d5d32107636d5 100644
--- a/libc/test/src/string/memory_utils/CMakeLists.txt
+++ b/libc/test/src/string/memory_utils/CMakeLists.txt
@@ -3,6 +3,7 @@ add_libc_unittest(
   SUITE
     libc_string_unittests
   SRCS
+    address_test.cpp
     elements_test.cpp
     memory_access_test.cpp
     utils_test.cpp

diff  --git a/libc/test/src/string/memory_utils/address_test.cpp b/libc/test/src/string/memory_utils/address_test.cpp
new file mode 100644
index 0000000000000..d684494602e21
--- /dev/null
+++ b/libc/test/src/string/memory_utils/address_test.cpp
@@ -0,0 +1,80 @@
+#include "utils/UnitTest/Test.h"
+#include <src/string/memory_utils/address.h>
+
+namespace __llvm_libc {
+
+TEST(LlvmLibcAddress, AliasAreAddresses) {
+  ASSERT_TRUE(IsAddressType<SrcAddr<1>>::Value);
+  ASSERT_TRUE(IsAddressType<DstAddr<1>>::Value);
+  ASSERT_TRUE(IsAddressType<NtSrcAddr<1>>::Value);
+  ASSERT_TRUE(IsAddressType<NtDstAddr<1>>::Value);
+}
+
+TEST(LlvmLibcAddress, AliasHaveRightPermissions) {
+  ASSERT_TRUE(SrcAddr<1>::IS_READ);
+  ASSERT_TRUE(NtSrcAddr<1>::IS_READ);
+  ASSERT_TRUE(DstAddr<1>::IS_WRITE);
+  ASSERT_TRUE(NtDstAddr<1>::IS_WRITE);
+}
+
+TEST(LlvmLibcAddress, AliasHaveRightSemantic) {
+  ASSERT_EQ(SrcAddr<1>::TEMPORALITY, Temporality::TEMPORAL);
+  ASSERT_EQ(DstAddr<1>::TEMPORALITY, Temporality::TEMPORAL);
+  ASSERT_EQ(NtSrcAddr<1>::TEMPORALITY, Temporality::NON_TEMPORAL);
+  ASSERT_EQ(NtDstAddr<1>::TEMPORALITY, Temporality::NON_TEMPORAL);
+}
+
+TEST(LlvmLibcAddress, AliasHaveRightAlignment) {
+  ASSERT_EQ(SrcAddr<1>::ALIGNMENT, size_t(1));
+  ASSERT_EQ(SrcAddr<4>::ALIGNMENT, size_t(4));
+}
+
+TEST(LlvmLibcAddress, NarrowAlignment) {
+  // Address 8-byte aligned, offset by 8.
+  ASSERT_EQ(offsetAddr<8>(SrcAddr<8>(nullptr)).ALIGNMENT, 8UL);
+  // Address 16-byte aligned, offset by 4.
+  ASSERT_EQ(offsetAddr<4>(SrcAddr<16>(nullptr)).ALIGNMENT, 4UL);
+  // Address 4-byte aligned, offset by 16.
+  ASSERT_EQ(offsetAddr<16>(SrcAddr<4>(nullptr)).ALIGNMENT, 4UL);
+  // Address 4-byte aligned, offset by 1.
+  ASSERT_EQ(offsetAddr<1>(SrcAddr<4>(nullptr)).ALIGNMENT, 1UL);
+  // Address 4-byte aligned, offset by 2.
+  ASSERT_EQ(offsetAddr<2>(SrcAddr<4>(nullptr)).ALIGNMENT, 2UL);
+  // Address 4-byte aligned, offset by 6.
+  ASSERT_EQ(offsetAddr<6>(SrcAddr<4>(nullptr)).ALIGNMENT, 2UL);
+  // Address 4-byte aligned, offset by 10.
+  ASSERT_EQ(offsetAddr<10>(SrcAddr<4>(nullptr)).ALIGNMENT, 2UL);
+  // Address 8-byte aligned, offset by 6.
+  ASSERT_EQ(offsetAddr<6>(SrcAddr<8>(nullptr)).ALIGNMENT, 2UL);
+}
+
+TEST(LlvmLibcAddress, OffsetAddr) {
+  ubyte a;
+  SrcAddr<1> addr(&a);
+  ASSERT_EQ((const void *)offsetAddr<4>(addr).ptr(), (const void *)(&a + 4));
+  ASSERT_EQ((const void *)offsetAddr<32>(addr).ptr(), (const void *)(&a + 32));
+}
+
+TEST(LlvmLibcAddress, AssumeAligned) {
+  SrcAddr<16> addr(nullptr);
+  ASSERT_EQ(offsetAddrAssumeAligned<8>(addr, 0).ALIGNMENT, 8UL);
+  ASSERT_EQ(offsetAddrAssumeAligned<1>(addr, 0).ALIGNMENT, 1UL);
+  ASSERT_EQ(offsetAddrMultiplesOf<4>(addr, 0).ALIGNMENT, 4UL);
+  ASSERT_EQ(offsetAddrMultiplesOf<32>(addr, 0).ALIGNMENT, 16UL);
+}
+
+TEST(LlvmLibcAddress, offsetAddrAssumeAligned) {
+  ubyte a;
+  SrcAddr<1> addr(&a);
+  ASSERT_EQ((const void *)offsetAddrAssumeAligned<1>(addr, 17).ptr(),
+            (const void *)(&a + 17));
+}
+
+TEST(LlvmLibcAddress, offsetAddrMultiplesOf) {
+  ubyte a;
+  SrcAddr<1> addr(&a);
+  ASSERT_EQ((const void *)offsetAddrMultiplesOf<4>(addr, 16).ptr(),
+            (const void *)(&a + 16));
+}
+
+} // namespace __llvm_libc


        


More information about the libc-commits mailing list