Bug 1867925 - Mark some storage-access-api tests as intermittent after wpt-sync....
[gecko.git] / third_party / highway / hwy / aligned_allocator_test.cc
blob5d678a0de6cc1c75696f198700d8deaaf62ab48a
1 // Copyright 2020 Google LLC
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
16 #include "hwy/aligned_allocator.h"
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <stdlib.h> // malloc
22 #include <array>
23 #include <random>
24 #include <set>
25 #include <vector>
27 #include "gtest/gtest.h"
29 namespace {
31 // Sample object that keeps track on an external counter of how many times was
32 // the explicit constructor and destructor called.
33 template <size_t N>
34 class SampleObject {
35 public:
36 SampleObject() { data_[0] = 'a'; }
37 explicit SampleObject(int* counter) : counter_(counter) {
38 if (counter) (*counter)++;
39 data_[0] = 'b';
42 ~SampleObject() {
43 if (counter_) (*counter_)--;
46 static_assert(N > sizeof(int*), "SampleObject size too small.");
47 int* counter_ = nullptr;
48 char data_[N - sizeof(int*)];
51 class FakeAllocator {
52 public:
53 // static AllocPtr and FreePtr member to be used with the aligned
54 // allocator. These functions calls the private non-static members.
55 static void* StaticAlloc(void* opaque, size_t bytes) {
56 return reinterpret_cast<FakeAllocator*>(opaque)->Alloc(bytes);
58 static void StaticFree(void* opaque, void* memory) {
59 return reinterpret_cast<FakeAllocator*>(opaque)->Free(memory);
62 // Returns the number of pending allocations to be freed.
63 size_t PendingAllocs() { return allocs_.size(); }
65 private:
66 void* Alloc(size_t bytes) {
67 void* ret = malloc(bytes);
68 allocs_.insert(ret);
69 return ret;
71 void Free(void* memory) {
72 if (!memory) return;
73 EXPECT_NE(allocs_.end(), allocs_.find(memory));
74 allocs_.erase(memory);
75 free(memory);
78 std::set<void*> allocs_;
81 } // namespace
83 namespace hwy {
85 class AlignedAllocatorTest : public testing::Test {};
87 TEST(AlignedAllocatorTest, FreeNullptr) {
88 // Calling free with a nullptr is always ok.
89 FreeAlignedBytes(/*aligned_pointer=*/nullptr, /*free_ptr=*/nullptr,
90 /*opaque_ptr=*/nullptr);
93 TEST(AlignedAllocatorTest, Log2) {
94 EXPECT_EQ(0u, detail::ShiftCount(1));
95 EXPECT_EQ(1u, detail::ShiftCount(2));
96 EXPECT_EQ(3u, detail::ShiftCount(8));
99 // Allocator returns null when it detects overflow of items * sizeof(T).
100 TEST(AlignedAllocatorTest, Overflow) {
101 constexpr size_t max = ~size_t(0);
102 constexpr size_t msb = (max >> 1) + 1;
103 using Size5 = std::array<uint8_t, 5>;
104 using Size10 = std::array<uint8_t, 10>;
105 EXPECT_EQ(nullptr,
106 detail::AllocateAlignedItems<uint32_t>(max / 2, nullptr, nullptr));
107 EXPECT_EQ(nullptr,
108 detail::AllocateAlignedItems<uint32_t>(max / 3, nullptr, nullptr));
109 EXPECT_EQ(nullptr,
110 detail::AllocateAlignedItems<Size5>(max / 4, nullptr, nullptr));
111 EXPECT_EQ(nullptr,
112 detail::AllocateAlignedItems<uint16_t>(msb, nullptr, nullptr));
113 EXPECT_EQ(nullptr,
114 detail::AllocateAlignedItems<double>(msb + 1, nullptr, nullptr));
115 EXPECT_EQ(nullptr,
116 detail::AllocateAlignedItems<Size10>(msb / 4, nullptr, nullptr));
119 TEST(AlignedAllocatorTest, AllocDefaultPointers) {
120 const size_t kSize = 7777;
121 void* ptr = AllocateAlignedBytes(kSize, /*alloc_ptr=*/nullptr,
122 /*opaque_ptr=*/nullptr);
123 ASSERT_NE(nullptr, ptr);
124 // Make sure the pointer is actually aligned.
125 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % HWY_ALIGNMENT);
126 char* p = static_cast<char*>(ptr);
127 size_t ret = 0;
128 for (size_t i = 0; i < kSize; i++) {
129 // Performs a computation using p[] to prevent it being optimized away.
130 p[i] = static_cast<char>(i & 0x7F);
131 if (i) ret += static_cast<size_t>(p[i] * p[i - 1]);
133 EXPECT_NE(0U, ret);
134 FreeAlignedBytes(ptr, /*free_ptr=*/nullptr, /*opaque_ptr=*/nullptr);
137 TEST(AlignedAllocatorTest, EmptyAlignedUniquePtr) {
138 AlignedUniquePtr<SampleObject<32>> ptr(nullptr, AlignedDeleter());
139 AlignedUniquePtr<SampleObject<32>[]> arr(nullptr, AlignedDeleter());
142 TEST(AlignedAllocatorTest, EmptyAlignedFreeUniquePtr) {
143 AlignedFreeUniquePtr<SampleObject<32>> ptr(nullptr, AlignedFreer());
144 AlignedFreeUniquePtr<SampleObject<32>[]> arr(nullptr, AlignedFreer());
147 TEST(AlignedAllocatorTest, CustomAlloc) {
148 FakeAllocator fake_alloc;
150 const size_t kSize = 7777;
151 void* ptr =
152 AllocateAlignedBytes(kSize, &FakeAllocator::StaticAlloc, &fake_alloc);
153 ASSERT_NE(nullptr, ptr);
154 // We should have only requested one alloc from the allocator.
155 EXPECT_EQ(1U, fake_alloc.PendingAllocs());
156 // Make sure the pointer is actually aligned.
157 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % HWY_ALIGNMENT);
158 FreeAlignedBytes(ptr, &FakeAllocator::StaticFree, &fake_alloc);
159 EXPECT_EQ(0U, fake_alloc.PendingAllocs());
162 TEST(AlignedAllocatorTest, MakeUniqueAlignedDefaultConstructor) {
164 auto ptr = MakeUniqueAligned<SampleObject<24>>();
165 // Default constructor sets the data_[0] to 'a'.
166 EXPECT_EQ('a', ptr->data_[0]);
167 EXPECT_EQ(nullptr, ptr->counter_);
171 TEST(AlignedAllocatorTest, MakeUniqueAligned) {
172 int counter = 0;
174 // Creates the object, initializes it with the explicit constructor and
175 // returns an unique_ptr to it.
176 auto ptr = MakeUniqueAligned<SampleObject<24>>(&counter);
177 EXPECT_EQ(1, counter);
178 // Custom constructor sets the data_[0] to 'b'.
179 EXPECT_EQ('b', ptr->data_[0]);
181 EXPECT_EQ(0, counter);
184 TEST(AlignedAllocatorTest, MakeUniqueAlignedArray) {
185 int counter = 0;
187 // Creates the array of objects and initializes them with the explicit
188 // constructor.
189 auto arr = MakeUniqueAlignedArray<SampleObject<24>>(7, &counter);
190 EXPECT_EQ(7, counter);
191 for (size_t i = 0; i < 7; i++) {
192 // Custom constructor sets the data_[0] to 'b'.
193 EXPECT_EQ('b', arr[i].data_[0]) << "Where i = " << i;
196 EXPECT_EQ(0, counter);
199 TEST(AlignedAllocatorTest, AllocSingleInt) {
200 auto ptr = AllocateAligned<uint32_t>(1);
201 ASSERT_NE(nullptr, ptr.get());
202 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(ptr.get()) % HWY_ALIGNMENT);
203 // Force delete of the unique_ptr now to check that it doesn't crash.
204 ptr.reset(nullptr);
205 EXPECT_EQ(nullptr, ptr.get());
208 TEST(AlignedAllocatorTest, AllocMultipleInt) {
209 const size_t kSize = 7777;
210 auto ptr = AllocateAligned<uint32_t>(kSize);
211 ASSERT_NE(nullptr, ptr.get());
212 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(ptr.get()) % HWY_ALIGNMENT);
213 // ptr[i] is actually (*ptr.get())[i] which will use the operator[] of the
214 // underlying type chosen by AllocateAligned() for the std::unique_ptr.
215 EXPECT_EQ(&(ptr[0]) + 1, &(ptr[1]));
217 size_t ret = 0;
218 for (size_t i = 0; i < kSize; i++) {
219 // Performs a computation using ptr[] to prevent it being optimized away.
220 ptr[i] = static_cast<uint32_t>(i);
221 if (i) ret += ptr[i] * ptr[i - 1];
223 EXPECT_NE(0U, ret);
226 TEST(AlignedAllocatorTest, AllocateAlignedObjectWithoutDestructor) {
227 int counter = 0;
229 // This doesn't call the constructor.
230 auto obj = AllocateAligned<SampleObject<24>>(1);
231 obj[0].counter_ = &counter;
233 // Destroying the unique_ptr shouldn't have called the destructor of the
234 // SampleObject<24>.
235 EXPECT_EQ(0, counter);
238 TEST(AlignedAllocatorTest, MakeUniqueAlignedArrayWithCustomAlloc) {
239 FakeAllocator fake_alloc;
240 int counter = 0;
242 // Creates the array of objects and initializes them with the explicit
243 // constructor.
244 auto arr = MakeUniqueAlignedArrayWithAlloc<SampleObject<24>>(
245 7, FakeAllocator::StaticAlloc, FakeAllocator::StaticFree, &fake_alloc,
246 &counter);
247 ASSERT_NE(nullptr, arr.get());
248 // An array should still only call a single allocation.
249 EXPECT_EQ(1u, fake_alloc.PendingAllocs());
250 EXPECT_EQ(7, counter);
251 for (size_t i = 0; i < 7; i++) {
252 // Custom constructor sets the data_[0] to 'b'.
253 EXPECT_EQ('b', arr[i].data_[0]) << "Where i = " << i;
256 EXPECT_EQ(0, counter);
257 EXPECT_EQ(0u, fake_alloc.PendingAllocs());
260 TEST(AlignedAllocatorTest, DefaultInit) {
261 // The test is whether this compiles. Default-init is useful for output params
262 // and per-thread storage.
263 std::vector<AlignedUniquePtr<int[]>> ptrs;
264 std::vector<AlignedFreeUniquePtr<double[]>> free_ptrs;
265 ptrs.resize(128);
266 free_ptrs.resize(128);
267 // The following is to prevent elision of the pointers.
268 std::mt19937 rng(129); // Emscripten lacks random_device.
269 std::uniform_int_distribution<size_t> dist(0, 127);
270 ptrs[dist(rng)] = MakeUniqueAlignedArray<int>(123);
271 free_ptrs[dist(rng)] = AllocateAligned<double>(456);
272 // "Use" pointer without resorting to printf. 0 == 0. Can't shift by 64.
273 const auto addr1 = reinterpret_cast<uintptr_t>(ptrs[dist(rng)].get());
274 const auto addr2 = reinterpret_cast<uintptr_t>(free_ptrs[dist(rng)].get());
275 constexpr size_t kBits = sizeof(uintptr_t) * 8;
276 EXPECT_EQ((addr1 >> (kBits - 1)) >> (kBits - 1),
277 (addr2 >> (kBits - 1)) >> (kBits - 1));
280 } // namespace hwy