1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "partition_alloc/starscan/scan_loop.h"
6
7 #include <algorithm>
8
9 #include "build/build_config.h"
10 #include "partition_alloc/partition_alloc_base/cpu.h"
11 #include "partition_alloc/partition_alloc_buildflags.h"
12 #include "partition_alloc/partition_alloc_config.h"
13
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 #if BUILDFLAG(HAS_64_BIT_POINTERS)
17
18 namespace partition_alloc::internal {
19
20 namespace {
21
22 class TestScanLoop final : public ScanLoop<TestScanLoop> {
23 friend class ScanLoop<TestScanLoop>;
24
25 public:
TestScanLoop(SimdSupport ss)26 explicit TestScanLoop(SimdSupport ss) : ScanLoop(ss) {}
27
visited() const28 size_t visited() const { return visited_; }
29
Reset()30 void Reset() { visited_ = 0; }
31
32 private:
33 static constexpr uintptr_t kRegularPoolMask = 0xffffff0000000000;
34 static constexpr uintptr_t kBasePtr = 0x0000560000000000;
35
RegularPoolBase()36 static uintptr_t RegularPoolBase() { return kBasePtr; }
RegularPoolMask()37 static uintptr_t RegularPoolMask() { return kRegularPoolMask; }
38
CheckPointer(uintptr_t maybe_ptr)39 void CheckPointer(uintptr_t maybe_ptr) { ++visited_; }
40
41 size_t visited_ = 0;
42 };
43
44 static constexpr uintptr_t kValidPtr = 0x000056789abcdef0;
45 static constexpr uintptr_t kInvalidPtr = 0x0000aaaaaaaaaaaa;
46 static constexpr uintptr_t kZeroPtr = 0x0;
47
48 // Tests all possible compbinations of incoming args.
49 template <size_t Alignment, typename... Args>
TestOnRangeWithAlignment(TestScanLoop & sl,size_t expected_visited,Args...args)50 void TestOnRangeWithAlignment(TestScanLoop& sl,
51 size_t expected_visited,
52 Args... args) {
53 alignas(Alignment) uintptr_t range[] = {args...};
54 std::sort(std::begin(range), std::end(range));
55 do {
56 sl.Run(reinterpret_cast<uintptr_t>(std::begin(range)),
57 reinterpret_cast<uintptr_t>(std::end(range)));
58 EXPECT_EQ(expected_visited, sl.visited());
59 sl.Reset();
60 } while (std::next_permutation(std::begin(range), std::end(range)));
61 }
62
63 } // namespace
64
TEST(PartitionAllocScanLoopTest,UnvectorizedWithRegularPool)65 TEST(PartitionAllocScanLoopTest, UnvectorizedWithRegularPool) {
66 {
67 TestScanLoop sl(SimdSupport::kUnvectorized);
68 TestOnRangeWithAlignment<8>(sl, 0u, kInvalidPtr, kInvalidPtr, kInvalidPtr);
69 }
70 {
71 TestScanLoop sl(SimdSupport::kUnvectorized);
72 TestOnRangeWithAlignment<8>(sl, 1u, kValidPtr, kInvalidPtr, kInvalidPtr);
73 }
74 {
75 TestScanLoop sl(SimdSupport::kUnvectorized);
76 TestOnRangeWithAlignment<8>(sl, 2u, kValidPtr, kValidPtr, kInvalidPtr);
77 }
78 {
79 // Make sure zeros are skipped.
80 TestScanLoop sl(SimdSupport::kUnvectorized);
81 TestOnRangeWithAlignment<8>(sl, 1u, kValidPtr, kInvalidPtr, kZeroPtr);
82 }
83 }
84
85 #if defined(ARCH_CPU_X86_64)
TEST(PartitionAllocScanLoopTest,VectorizedSSE4)86 TEST(PartitionAllocScanLoopTest, VectorizedSSE4) {
87 base::CPU cpu;
88 if (!cpu.has_sse41()) {
89 return;
90 }
91 {
92 TestScanLoop sl(SimdSupport::kSSE41);
93 TestOnRangeWithAlignment<16>(sl, 0u, kInvalidPtr, kInvalidPtr, kInvalidPtr);
94 }
95 {
96 TestScanLoop sl(SimdSupport::kSSE41);
97 TestOnRangeWithAlignment<16>(sl, 1u, kValidPtr, kInvalidPtr, kInvalidPtr);
98 }
99 {
100 TestScanLoop sl(SimdSupport::kSSE41);
101 TestOnRangeWithAlignment<16>(sl, 2u, kValidPtr, kValidPtr, kInvalidPtr);
102 }
103 {
104 TestScanLoop sl(SimdSupport::kSSE41);
105 TestOnRangeWithAlignment<16>(sl, 3u, kValidPtr, kValidPtr, kValidPtr);
106 }
107 }
108
TEST(PartitionAllocScanLoopTest,VectorizedAVX2)109 TEST(PartitionAllocScanLoopTest, VectorizedAVX2) {
110 base::CPU cpu;
111 if (!cpu.has_avx2()) {
112 return;
113 }
114 {
115 TestScanLoop sl(SimdSupport::kAVX2);
116 TestOnRangeWithAlignment<32>(sl, 0u, kInvalidPtr, kInvalidPtr, kInvalidPtr,
117 kInvalidPtr, kInvalidPtr);
118 }
119 {
120 TestScanLoop sl(SimdSupport::kAVX2);
121 TestOnRangeWithAlignment<32>(sl, 1u, kValidPtr, kInvalidPtr, kInvalidPtr,
122 kInvalidPtr, kInvalidPtr);
123 }
124 {
125 TestScanLoop sl(SimdSupport::kAVX2);
126 TestOnRangeWithAlignment<32>(sl, 2u, kValidPtr, kValidPtr, kInvalidPtr,
127 kInvalidPtr, kInvalidPtr);
128 }
129 {
130 TestScanLoop sl(SimdSupport::kAVX2);
131 TestOnRangeWithAlignment<32>(sl, 3u, kValidPtr, kValidPtr, kValidPtr,
132 kInvalidPtr, kInvalidPtr);
133 }
134 {
135 TestScanLoop sl(SimdSupport::kAVX2);
136 TestOnRangeWithAlignment<32>(sl, 4u, kValidPtr, kValidPtr, kValidPtr,
137 kValidPtr, kInvalidPtr);
138 }
139 {
140 // Check that the residual pointer is also visited.
141 TestScanLoop sl(SimdSupport::kAVX2);
142 TestOnRangeWithAlignment<32>(sl, 5u, kValidPtr, kValidPtr, kValidPtr,
143 kValidPtr, kValidPtr);
144 }
145 }
146 #endif // defined(ARCH_CPU_X86_64)
147
148 #if PA_CONFIG(STARSCAN_NEON_SUPPORTED)
TEST(PartitionAllocScanLoopTest,VectorizedNEON)149 TEST(PartitionAllocScanLoopTest, VectorizedNEON) {
150 {
151 TestScanLoop sl(SimdSupport::kNEON);
152 TestOnRangeWithAlignment<16>(sl, 0u, kInvalidPtr, kInvalidPtr, kInvalidPtr);
153 }
154 {
155 TestScanLoop sl(SimdSupport::kNEON);
156 TestOnRangeWithAlignment<16>(sl, 1u, kValidPtr, kInvalidPtr, kInvalidPtr);
157 }
158 {
159 TestScanLoop sl(SimdSupport::kNEON);
160 TestOnRangeWithAlignment<16>(sl, 2u, kValidPtr, kValidPtr, kInvalidPtr);
161 }
162 {
163 TestScanLoop sl(SimdSupport::kNEON);
164 TestOnRangeWithAlignment<16>(sl, 3u, kValidPtr, kValidPtr, kValidPtr);
165 }
166 {
167 // Don't visit zeroes.
168 TestScanLoop sl(SimdSupport::kNEON);
169 TestOnRangeWithAlignment<16>(sl, 1u, kInvalidPtr, kValidPtr, kZeroPtr);
170 }
171 }
172 #endif // PA_CONFIG(STARSCAN_NEON_SUPPORTED)
173
174 } // namespace partition_alloc::internal
175
176 #endif // BUILDFLAG(HAS_64_BIT_POINTERS)
177