1 //===-- Unittests for memory_utils ----------------------------------------===//
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 #define LLVM_LIBC_UNITTEST_OBSERVE 1
10
11 #include "src/string/memory_utils/elements.h"
12 #include "utils/CPP/Array.h"
13 #include "utils/CPP/ArrayRef.h"
14 #include "utils/UnitTest/Test.h"
15
16 #include <stdio.h>
17 #include <string.h>
18
19 namespace __llvm_libc {
20
21 static constexpr const size_t kMaxBuffer = 32;
22
23 struct BufferAccess : cpp::Array<char, kMaxBuffer + 1> {
BufferAccess__llvm_libc::BufferAccess24 BufferAccess() { Reset(); }
Reset__llvm_libc::BufferAccess25 void Reset() {
26 for (auto &value : *this)
27 value = '0';
28 this->operator[](kMaxBuffer) = '\0';
29 }
Touch__llvm_libc::BufferAccess30 void Touch(ptrdiff_t offset, size_t size) {
31 if (offset < 0)
32 return;
33 for (size_t i = 0; i < size; ++i)
34 ++(*this)[offset + i];
35 }
operator const char*__llvm_libc::BufferAccess36 operator const char *() const { return this->data(); }
37 };
38
39 struct Buffer {
Offset__llvm_libc::Buffer40 ptrdiff_t Offset(const char *ptr) const {
41 const bool contained = ptr >= data.begin() && ptr < data.end();
42 return contained ? ptr - data.begin() : -1;
43 }
Reset__llvm_libc::Buffer44 void Reset() {
45 reads.Reset();
46 writes.Reset();
47 }
48 cpp::Array<char, kMaxBuffer> data;
49 BufferAccess __attribute__((aligned(64))) reads;
50 BufferAccess __attribute__((aligned(64))) writes;
51 };
52
53 struct MemoryAccessObserver {
ObserveRead__llvm_libc::MemoryAccessObserver54 void ObserveRead(const char *ptr, size_t size) {
55 Buffer1.reads.Touch(Buffer1.Offset(ptr), size);
56 Buffer2.reads.Touch(Buffer2.Offset(ptr), size);
57 }
58
ObserveWrite__llvm_libc::MemoryAccessObserver59 void ObserveWrite(const char *ptr, size_t size) {
60 Buffer1.writes.Touch(Buffer1.Offset(ptr), size);
61 Buffer2.writes.Touch(Buffer2.Offset(ptr), size);
62 }
63
Reset__llvm_libc::MemoryAccessObserver64 void Reset() {
65 Buffer1.Reset();
66 Buffer2.Reset();
67 }
68
69 Buffer Buffer1;
70 Buffer Buffer2;
71 };
72
73 MemoryAccessObserver Observer;
74
75 template <size_t Size> struct TestingElement {
76 static constexpr size_t kSize = Size;
77
Copy__llvm_libc::TestingElement78 static void Copy(char *__restrict dst, const char *__restrict src) {
79 Observer.ObserveRead(src, kSize);
80 Observer.ObserveWrite(dst, kSize);
81 }
82
Equals__llvm_libc::TestingElement83 static bool Equals(const char *lhs, const char *rhs) {
84 Observer.ObserveRead(lhs, kSize);
85 Observer.ObserveRead(rhs, kSize);
86 return true;
87 }
88
ThreeWayCompare__llvm_libc::TestingElement89 static int ThreeWayCompare(const char *lhs, const char *rhs) {
90 Observer.ObserveRead(lhs, kSize);
91 Observer.ObserveRead(rhs, kSize);
92 return 0;
93 }
94
SplatSet__llvm_libc::TestingElement95 static void SplatSet(char *dst, const unsigned char value) {
96 Observer.ObserveWrite(dst, kSize);
97 }
98 };
99
100 using Types = testing::TypeList<
101 TestingElement<1>, // 1 Byte
102 TestingElement<2>, // 2 Bytes
103 TestingElement<4>, // 4 Bytes
104 Repeated<TestingElement<2>, 3>, // 6 Bytes
105 Chained<TestingElement<4>, TestingElement<2>, TestingElement<1>> // 7 Bytes
106 >;
107
108 struct LlvmLibcTestAccessBase : public testing::Test {
109
110 template <typename HigherOrder, size_t Size, size_t Offset = 0>
checkOperations__llvm_libc::LlvmLibcTestAccessBase111 void checkOperations(const BufferAccess &expected) {
112 static const BufferAccess untouched;
113
114 Observer.Reset();
115 HigherOrder::Copy(dst_ptr() + Offset, src_ptr() + Offset, Size);
116 ASSERT_STREQ(src().writes, untouched);
117 ASSERT_STREQ(dst().reads, untouched);
118 ASSERT_STREQ(src().reads, expected);
119 ASSERT_STREQ(dst().writes, expected);
120 Observer.Reset();
121 HigherOrder::Equals(lhs_ptr() + Offset, rhs_ptr() + Offset, Size);
122 ASSERT_STREQ(lhs().writes, untouched);
123 ASSERT_STREQ(rhs().writes, untouched);
124 ASSERT_STREQ(lhs().reads, expected);
125 ASSERT_STREQ(rhs().reads, expected);
126 Observer.Reset();
127 HigherOrder::ThreeWayCompare(lhs_ptr() + Offset, rhs_ptr() + Offset, Size);
128 ASSERT_STREQ(lhs().writes, untouched);
129 ASSERT_STREQ(rhs().writes, untouched);
130 ASSERT_STREQ(lhs().reads, expected);
131 ASSERT_STREQ(rhs().reads, expected);
132 Observer.Reset();
133 HigherOrder::SplatSet(dst_ptr() + Offset, 5, Size);
134 ASSERT_STREQ(src().reads, untouched);
135 ASSERT_STREQ(src().writes, untouched);
136 ASSERT_STREQ(dst().reads, untouched);
137 ASSERT_STREQ(dst().writes, expected);
138 }
139
checkMaxAccess__llvm_libc::LlvmLibcTestAccessBase140 void checkMaxAccess(const BufferAccess &expected, int max) {
141 for (size_t i = 0; i < kMaxBuffer; ++i) {
142 int value = (int)expected[i] - '0';
143 ASSERT_GE(value, 0);
144 ASSERT_LE(value, max);
145 }
146 }
147
148 private:
lhs__llvm_libc::LlvmLibcTestAccessBase149 const Buffer &lhs() const { return Observer.Buffer1; }
rhs__llvm_libc::LlvmLibcTestAccessBase150 const Buffer &rhs() const { return Observer.Buffer2; }
src__llvm_libc::LlvmLibcTestAccessBase151 const Buffer &src() const { return Observer.Buffer2; }
dst__llvm_libc::LlvmLibcTestAccessBase152 const Buffer &dst() const { return Observer.Buffer1; }
dst__llvm_libc::LlvmLibcTestAccessBase153 Buffer &dst() { return Observer.Buffer1; }
154
dst_ptr__llvm_libc::LlvmLibcTestAccessBase155 char *dst_ptr() { return dst().data.begin(); }
src_ptr__llvm_libc::LlvmLibcTestAccessBase156 const char *src_ptr() { return src().data.begin(); }
lhs_ptr__llvm_libc::LlvmLibcTestAccessBase157 const char *lhs_ptr() { return lhs().data.begin(); }
rhs_ptr__llvm_libc::LlvmLibcTestAccessBase158 const char *rhs_ptr() { return rhs().data.begin(); }
159 };
160
161 template <typename ParamType>
162 struct LlvmLibcTestAccessTail : public LlvmLibcTestAccessBase {
163
TearDown__llvm_libc::LlvmLibcTestAccessTail164 void TearDown() override {
165 static constexpr size_t Size = 10;
166
167 BufferAccess expected;
168 expected.Touch(Size - ParamType::kSize, ParamType::kSize);
169
170 checkMaxAccess(expected, 1);
171 checkOperations<Tail<ParamType>, Size>(expected);
172 }
173 };
TYPED_TEST_F(LlvmLibcTestAccessTail,Operations,Types)174 TYPED_TEST_F(LlvmLibcTestAccessTail, Operations, Types) {}
175
176 template <typename ParamType>
177 struct LlvmLibcTestAccessHeadTail : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessHeadTail178 void TearDown() override {
179 static constexpr size_t Size = 10;
180
181 BufferAccess expected;
182 expected.Touch(0, ParamType::kSize);
183 expected.Touch(Size - ParamType::kSize, ParamType::kSize);
184
185 checkMaxAccess(expected, 2);
186 checkOperations<HeadTail<ParamType>, Size>(expected);
187 }
188 };
TYPED_TEST_F(LlvmLibcTestAccessHeadTail,Operations,Types)189 TYPED_TEST_F(LlvmLibcTestAccessHeadTail, Operations, Types) {}
190
191 template <typename ParamType>
192 struct LlvmLibcTestAccessLoop : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessLoop193 void TearDown() override {
194 static constexpr size_t Size = 20;
195
196 BufferAccess expected;
197 for (size_t i = 0; i < Size - ParamType::kSize; i += ParamType::kSize)
198 expected.Touch(i, ParamType::kSize);
199 expected.Touch(Size - ParamType::kSize, ParamType::kSize);
200
201 checkMaxAccess(expected, 2);
202 checkOperations<Loop<ParamType>, Size>(expected);
203 }
204 };
TYPED_TEST_F(LlvmLibcTestAccessLoop,Operations,Types)205 TYPED_TEST_F(LlvmLibcTestAccessLoop, Operations, Types) {}
206
207 template <typename ParamType>
208 struct LlvmLibcTestAccessAlignedAccess : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessAlignedAccess209 void TearDown() override {
210 static constexpr size_t Size = 10;
211 static constexpr size_t Offset = 2;
212 using AlignmentT = TestingElement<4>;
213
214 BufferAccess expected;
215 expected.Touch(Offset, AlignmentT::kSize);
216 expected.Touch(AlignmentT::kSize, ParamType::kSize);
217 expected.Touch(Offset + Size - ParamType::kSize, ParamType::kSize);
218
219 checkMaxAccess(expected, 3);
220 checkOperations<Align<AlignmentT, Arg::_1>::Then<HeadTail<ParamType>>, Size,
221 Offset>(expected);
222 checkOperations<Align<AlignmentT, Arg::_2>::Then<HeadTail<ParamType>>, Size,
223 Offset>(expected);
224 }
225 };
TYPED_TEST_F(LlvmLibcTestAccessAlignedAccess,Operations,Types)226 TYPED_TEST_F(LlvmLibcTestAccessAlignedAccess, Operations, Types) {}
227
228 } // namespace __llvm_libc
229