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/__support/CPP/Array.h"
12 #include "src/__support/CPP/ArrayRef.h"
13 #include "src/string/memory_utils/elements.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 SIZE = Size;
77
copy__llvm_libc::TestingElement78 static void copy(char *__restrict dst, const char *__restrict src) {
79 Observer.ObserveRead(src, SIZE);
80 Observer.ObserveWrite(dst, SIZE);
81 }
82
equals__llvm_libc::TestingElement83 static bool equals(const char *lhs, const char *rhs) {
84 Observer.ObserveRead(lhs, SIZE);
85 Observer.ObserveRead(rhs, SIZE);
86 return true;
87 }
88
three_way_compare__llvm_libc::TestingElement89 static int three_way_compare(const char *lhs, const char *rhs) {
90 Observer.ObserveRead(lhs, SIZE);
91 Observer.ObserveRead(rhs, SIZE);
92 return 0;
93 }
94
splat_set__llvm_libc::TestingElement95 static void splat_set(char *dst, const unsigned char value) {
96 Observer.ObserveWrite(dst, SIZE);
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::three_way_compare(lhs_ptr() + Offset, rhs_ptr() + Offset,
128 Size);
129 ASSERT_STREQ(lhs().writes, untouched);
130 ASSERT_STREQ(rhs().writes, untouched);
131 ASSERT_STREQ(lhs().reads, expected);
132 ASSERT_STREQ(rhs().reads, expected);
133 Observer.Reset();
134 HigherOrder::splat_set(dst_ptr() + Offset, 5, Size);
135 ASSERT_STREQ(src().reads, untouched);
136 ASSERT_STREQ(src().writes, untouched);
137 ASSERT_STREQ(dst().reads, untouched);
138 ASSERT_STREQ(dst().writes, expected);
139 }
140
checkMaxAccess__llvm_libc::LlvmLibcTestAccessBase141 void checkMaxAccess(const BufferAccess &expected, int max) {
142 for (size_t i = 0; i < kMaxBuffer; ++i) {
143 int value = (int)expected[i] - '0';
144 ASSERT_GE(value, 0);
145 ASSERT_LE(value, max);
146 }
147 }
148
149 private:
lhs__llvm_libc::LlvmLibcTestAccessBase150 const Buffer &lhs() const { return Observer.Buffer1; }
rhs__llvm_libc::LlvmLibcTestAccessBase151 const Buffer &rhs() const { return Observer.Buffer2; }
src__llvm_libc::LlvmLibcTestAccessBase152 const Buffer &src() const { return Observer.Buffer2; }
dst__llvm_libc::LlvmLibcTestAccessBase153 const Buffer &dst() const { return Observer.Buffer1; }
dst__llvm_libc::LlvmLibcTestAccessBase154 Buffer &dst() { return Observer.Buffer1; }
155
dst_ptr__llvm_libc::LlvmLibcTestAccessBase156 char *dst_ptr() { return dst().data.begin(); }
src_ptr__llvm_libc::LlvmLibcTestAccessBase157 const char *src_ptr() { return src().data.begin(); }
lhs_ptr__llvm_libc::LlvmLibcTestAccessBase158 const char *lhs_ptr() { return lhs().data.begin(); }
rhs_ptr__llvm_libc::LlvmLibcTestAccessBase159 const char *rhs_ptr() { return rhs().data.begin(); }
160 };
161
162 template <typename ParamType>
163 struct LlvmLibcTestAccessTail : public LlvmLibcTestAccessBase {
164
TearDown__llvm_libc::LlvmLibcTestAccessTail165 void TearDown() override {
166 static constexpr size_t Size = 10;
167
168 BufferAccess expected;
169 expected.Touch(Size - ParamType::SIZE, ParamType::SIZE);
170
171 checkMaxAccess(expected, 1);
172 checkOperations<Tail<ParamType>, Size>(expected);
173 }
174 };
TYPED_TEST_F(LlvmLibcTestAccessTail,Operations,Types)175 TYPED_TEST_F(LlvmLibcTestAccessTail, Operations, Types) {}
176
177 template <typename ParamType>
178 struct LlvmLibcTestAccessHeadTail : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessHeadTail179 void TearDown() override {
180 static constexpr size_t Size = 10;
181
182 BufferAccess expected;
183 expected.Touch(0, ParamType::SIZE);
184 expected.Touch(Size - ParamType::SIZE, ParamType::SIZE);
185
186 checkMaxAccess(expected, 2);
187 checkOperations<HeadTail<ParamType>, Size>(expected);
188 }
189 };
TYPED_TEST_F(LlvmLibcTestAccessHeadTail,Operations,Types)190 TYPED_TEST_F(LlvmLibcTestAccessHeadTail, Operations, Types) {}
191
192 template <typename ParamType>
193 struct LlvmLibcTestAccessLoop : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessLoop194 void TearDown() override {
195 static constexpr size_t Size = 20;
196
197 BufferAccess expected;
198 for (size_t i = 0; i < Size - ParamType::SIZE; i += ParamType::SIZE)
199 expected.Touch(i, ParamType::SIZE);
200 expected.Touch(Size - ParamType::SIZE, ParamType::SIZE);
201
202 checkMaxAccess(expected, 2);
203 checkOperations<Loop<ParamType>, Size>(expected);
204 }
205 };
TYPED_TEST_F(LlvmLibcTestAccessLoop,Operations,Types)206 TYPED_TEST_F(LlvmLibcTestAccessLoop, Operations, Types) {}
207
208 template <typename ParamType>
209 struct LlvmLibcTestAccessAlignedAccess : public LlvmLibcTestAccessBase {
TearDown__llvm_libc::LlvmLibcTestAccessAlignedAccess210 void TearDown() override {
211 static constexpr size_t Size = 10;
212 static constexpr size_t Offset = 2;
213 using AlignmentT = TestingElement<4>;
214
215 BufferAccess expected;
216 expected.Touch(Offset, AlignmentT::SIZE);
217 expected.Touch(AlignmentT::SIZE, ParamType::SIZE);
218 expected.Touch(Offset + Size - ParamType::SIZE, ParamType::SIZE);
219
220 checkMaxAccess(expected, 3);
221 checkOperations<Align<AlignmentT, Arg::_1>::Then<HeadTail<ParamType>>, Size,
222 Offset>(expected);
223 checkOperations<Align<AlignmentT, Arg::_2>::Then<HeadTail<ParamType>>, Size,
224 Offset>(expected);
225 }
226 };
TYPED_TEST_F(LlvmLibcTestAccessAlignedAccess,Operations,Types)227 TYPED_TEST_F(LlvmLibcTestAccessAlignedAccess, Operations, Types) {}
228
229 } // namespace __llvm_libc
230