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