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