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