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