[libc-commits] [libc] 85314e9 - [libc] Add utils for memory functions

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Fri Jan 31 01:15:22 PST 2020


Author: Guillaume Chatelet
Date: 2020-01-31T10:15:02+01:00
New Revision: 85314e9b7a785e19aa63e17d96436ab35137f995

URL: https://github.com/llvm/llvm-project/commit/85314e9b7a785e19aa63e17d96436ab35137f995
DIFF: https://github.com/llvm/llvm-project/commit/85314e9b7a785e19aa63e17d96436ab35137f995.diff

LOG: [libc] Add utils for memory functions

Summary: This patch adds a few low level functions needed to build libc memory functions.

Reviewers: sivachandra, jfb

Subscribers: mgorny, MaskRay, tschuett, libc-commits, ckennelly

Tags: #libc-project

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

Added: 
    libc/src/string/memory_utils/CMakeLists.txt
    libc/src/string/memory_utils/cacheline_size.h.def
    libc/src/string/memory_utils/cacheline_size_aarch64.h.inc
    libc/src/string/memory_utils/cacheline_size_arm.h.inc
    libc/src/string/memory_utils/cacheline_size_ppc64.h.inc
    libc/src/string/memory_utils/cacheline_size_x86.h.inc
    libc/src/string/memory_utils/cacheline_size_x86_64.h.inc
    libc/src/string/memory_utils/utils.h
    libc/test/src/string/memory_utils/CMakeLists.txt
    libc/test/src/string/memory_utils/utils_test.cpp

Modified: 
    libc/src/string/CMakeLists.txt
    libc/test/src/string/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index b53da2118250..49b0afd2cedf 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -18,3 +18,5 @@ add_entrypoint_object(
   DEPENDS
     string_h
 )
+
+add_subdirectory(memory_utils)

diff  --git a/libc/src/string/memory_utils/CMakeLists.txt b/libc/src/string/memory_utils/CMakeLists.txt
new file mode 100644
index 000000000000..259ed0a75828
--- /dev/null
+++ b/libc/src/string/memory_utils/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_gen_header(
+  cacheline_size
+  DEF_FILE
+    cacheline_size.h.def
+  GEN_HDR
+    cacheline_size.h
+  PARAMS
+    machine_cacheline_size=cacheline_size_${LIBC_TARGET_MACHINE}.h.inc
+  DATA_FILES
+    cacheline_size_${LIBC_TARGET_MACHINE}.h.inc
+)
+
+add_header_library(
+  memory_utils
+  HDRS utils.h
+  DEPENDS cacheline_size
+)

diff  --git a/libc/src/string/memory_utils/cacheline_size.h.def b/libc/src/string/memory_utils/cacheline_size.h.def
new file mode 100644
index 000000000000..43583b53ffd2
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size.h.def
@@ -0,0 +1,27 @@
+//===---------------------- Cacheline Size Constant -----------------------===//
+//
+// 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_MEMORY_CONSTANTS_H
+#define LLVM_LIBC_SRC_MEMORY_CONSTANTS_H
+
+// LLVM_LIBC_CACHELINE_SIZE
+//
+// Explicitly defines the size of the L1 cache for purposes of alignment.
+//
+// NOTE: this macro should be replaced with the following C++17 features, when
+// those are generally available:
+//
+//   * `std::hardware_constructive_interference_size`
+//   * `std::hardware_destructive_interference_size`
+//
+// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
+// for more information.
+
+%%include_file(${machine_cacheline_size})
+
+#endif // LLVM_LIBC_SRC_MEMORY_CONSTANTS_H

diff  --git a/libc/src/string/memory_utils/cacheline_size_aarch64.h.inc b/libc/src/string/memory_utils/cacheline_size_aarch64.h.inc
new file mode 100644
index 000000000000..34d8cef5ba41
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size_aarch64.h.inc
@@ -0,0 +1,3 @@
+// We would need to read special register ctr_el0 to find out L1 dcache size.
+// This value is a good estimate based on a real aarch64 machine.
+#define LLVM_LIBC_CACHELINE_SIZE 64

diff  --git a/libc/src/string/memory_utils/cacheline_size_arm.h.inc b/libc/src/string/memory_utils/cacheline_size_arm.h.inc
new file mode 100644
index 000000000000..f3a43e02a4b2
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size_arm.h.inc
@@ -0,0 +1,9 @@
+// Cache line sizes for ARM: These values are not strictly correct since
+// cache line sizes depend on implementations, not architectures.  There
+// are even implementations with cache line sizes configurable at boot
+// time.
+#if defined(__ARM_ARCH_5T__) 
+#define LLVM_LIBC_CACHELINE_SIZE 32
+#elif defined(__ARM_ARCH_7A__)
+#define LLVM_LIBC_CACHELINE_SIZE 64
+#endif

diff  --git a/libc/src/string/memory_utils/cacheline_size_ppc64.h.inc b/libc/src/string/memory_utils/cacheline_size_ppc64.h.inc
new file mode 100644
index 000000000000..3c2699420285
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size_ppc64.h.inc
@@ -0,0 +1 @@
+#define LLVM_LIBC_CACHELINE_SIZE 128

diff  --git a/libc/src/string/memory_utils/cacheline_size_x86.h.inc b/libc/src/string/memory_utils/cacheline_size_x86.h.inc
new file mode 100644
index 000000000000..29da174a2a63
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size_x86.h.inc
@@ -0,0 +1 @@
+#define LLVM_LIBC_CACHELINE_SIZE 64

diff  --git a/libc/src/string/memory_utils/cacheline_size_x86_64.h.inc b/libc/src/string/memory_utils/cacheline_size_x86_64.h.inc
new file mode 100644
index 000000000000..29da174a2a63
--- /dev/null
+++ b/libc/src/string/memory_utils/cacheline_size_x86_64.h.inc
@@ -0,0 +1 @@
+#define LLVM_LIBC_CACHELINE_SIZE 64

diff  --git a/libc/src/string/memory_utils/utils.h b/libc/src/string/memory_utils/utils.h
new file mode 100644
index 000000000000..33df113213b5
--- /dev/null
+++ b/libc/src/string/memory_utils/utils.h
@@ -0,0 +1,60 @@
+//===---------------------------- Memory utils ----------------------------===//
+//
+// 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_MEMORY_UTILS_H
+#define LLVM_LIBC_SRC_MEMORY_UTILS_H
+
+#include "src/string/memory_utils/cacheline_size.h"
+
+#include <stddef.h> // size_t
+#include <stdint.h> // intptr_t / uintptr_t
+
+namespace __llvm_libc {
+
+// Return whether `value` is zero or a power of two.
+static constexpr bool is_power2_or_zero(size_t value) {
+  return (value & (value - 1U)) == 0;
+}
+
+// Return whether `value` is a power of two.
+static constexpr bool is_power2(size_t value) {
+  return value && is_power2_or_zero(value);
+}
+
+// Compile time version of log2 that handles 0.
+static constexpr size_t log2(size_t value) {
+  return (value == 0 || value == 1) ? 0 : 1 + log2(value / 2);
+}
+
+// Returns the first power of two preceding value or value if it is already a
+// power of two (or 0 when value is 0).
+static constexpr size_t le_power2(size_t value) {
+  return value == 0 ? value : 1ULL << log2(value);
+}
+
+// Returns the first power of two following value or value if it is already a
+// power of two (or 0 when value is 0).
+static constexpr size_t ge_power2(size_t value) {
+  return is_power2_or_zero(value) ? value : 1ULL << (log2(value) + 1);
+}
+
+template <size_t alignment> intptr_t offset_to_next_aligned(const void *ptr) {
+  static_assert(is_power2(alignment), "alignment must be a power of 2");
+  // The logic is not straightforward and involves unsigned modulo arithmetic
+  // but the generated code is as fast as it can be.
+  return -reinterpret_cast<uintptr_t>(ptr) & (alignment - 1U);
+}
+
+// Returns the offset from `ptr` to the next cache line.
+static intptr_t offset_to_next_cache_line(const void *ptr) {
+  return offset_to_next_aligned<LLVM_LIBC_CACHELINE_SIZE>(ptr);
+}
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MEMORY_UTILS_H

diff  --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt
index 152bcbbc2aed..258937c7f4f6 100644
--- a/libc/test/src/string/CMakeLists.txt
+++ b/libc/test/src/string/CMakeLists.txt
@@ -1,5 +1,7 @@
 add_libc_testsuite(libc_string_unittests)
 
+add_subdirectory(memory_utils)
+
 add_libc_unittest(
   strcat_test
   SUITE

diff  --git a/libc/test/src/string/memory_utils/CMakeLists.txt b/libc/test/src/string/memory_utils/CMakeLists.txt
new file mode 100644
index 000000000000..e3ec8eb40cf3
--- /dev/null
+++ b/libc/test/src/string/memory_utils/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_libc_unittest(
+  utils_test
+  SUITE
+    libc_string_unittests
+  SRCS
+    utils_test.cpp
+  DEPENDS
+    memory_utils
+    standalone_cpp
+)

diff  --git a/libc/test/src/string/memory_utils/utils_test.cpp b/libc/test/src/string/memory_utils/utils_test.cpp
new file mode 100644
index 000000000000..d3ae3ff2f3b4
--- /dev/null
+++ b/libc/test/src/string/memory_utils/utils_test.cpp
@@ -0,0 +1,99 @@
+//===-------------------- Unittests for memory_utils ----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/string/memory_utils/utils.h"
+#include "utils/CPP/ArrayRef.h"
+#include "utils/UnitTest/Test.h"
+
+namespace __llvm_libc {
+
+TEST(UtilsTest, IsPowerOfTwoOrZero) {
+  static const cpp::ArrayRef<bool> kExpectedValues({
+      1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 0-15
+      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63
+      1                                               // 64
+  });
+  for (size_t i = 0; i < kExpectedValues.size(); ++i)
+    EXPECT_EQ(is_power2_or_zero(i), kExpectedValues[i]);
+}
+
+TEST(UtilsTest, IsPowerOfTwo) {
+  static const cpp::ArrayRef<bool> kExpectedValues({
+      0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 0-15
+      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63
+      1                                               // 64
+  });
+  for (size_t i = 0; i < kExpectedValues.size(); ++i)
+    EXPECT_EQ(is_power2(i), kExpectedValues[i]);
+}
+
+TEST(UtilsTest, Log2) {
+  static const cpp::ArrayRef<size_t> kExpectedValues({
+      0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 0-15
+      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16-31
+      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 32-47
+      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 48-63
+      6                                               // 64
+  });
+  for (size_t i = 0; i < kExpectedValues.size(); ++i)
+    EXPECT_EQ(log2(i), kExpectedValues[i]);
+}
+
+TEST(UtilsTest, LEPowerOf2) {
+  static const cpp::ArrayRef<size_t> kExpectedValues({
+      0,  1,  2,  2,  4,  4,  4,  4,  8,  8,  8,  8,  8,  8,  8,  8,  // 0-15
+      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, // 16-31
+      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 32-47
+      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 48-63
+      64                                                              // 64
+  });
+  for (size_t i = 0; i < kExpectedValues.size(); ++i)
+    EXPECT_EQ(le_power2(i), kExpectedValues[i]);
+}
+
+TEST(UtilsTest, GEPowerOf2) {
+  static const cpp::ArrayRef<size_t> kExpectedValues({
+      0,  1,  2,  4,  4,  8,  8,  8,  8,  16, 16, 16, 16, 16, 16, 16, // 0-15
+      16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 16-31
+      32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 32-47
+      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 48-63
+      64, 128                                                         // 64-65
+  });
+  for (size_t i = 0; i < kExpectedValues.size(); ++i)
+    EXPECT_EQ(ge_power2(i), kExpectedValues[i]);
+}
+
+using I = intptr_t;
+
+// Converts an offset into a pointer.
+const void *forge(size_t offset) {
+  return reinterpret_cast<const void *>(offset);
+};
+
+TEST(UtilsTest, OffsetToNextAligned) {
+  EXPECT_EQ(offset_to_next_aligned<16>(forge(0)), I(0));
+  EXPECT_EQ(offset_to_next_aligned<16>(forge(1)), I(15));
+  EXPECT_EQ(offset_to_next_aligned<16>(forge(16)), I(0));
+  EXPECT_EQ(offset_to_next_aligned<16>(forge(15)), I(1));
+  EXPECT_EQ(offset_to_next_aligned<32>(forge(16)), I(16));
+}
+
+TEST(UtilsTest, OffsetToNextCacheLine) {
+  EXPECT_GT(LLVM_LIBC_CACHELINE_SIZE, 0);
+  EXPECT_EQ(offset_to_next_cache_line(forge(0)), I(0));
+  EXPECT_EQ(offset_to_next_cache_line(forge(1)),
+            I(LLVM_LIBC_CACHELINE_SIZE - 1));
+  EXPECT_EQ(offset_to_next_cache_line(forge(LLVM_LIBC_CACHELINE_SIZE)), I(0));
+  EXPECT_EQ(offset_to_next_cache_line(forge(LLVM_LIBC_CACHELINE_SIZE - 1)),
+            I(1));
+}
+} // namespace __llvm_libc


        


More information about the libc-commits mailing list