1 | //=== - llvm/unittest/Support/TrailingObjectsTest.cpp ---------------------===// |
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 | #include "llvm/Support/TrailingObjects.h" |
10 | #include "gtest/gtest.h" |
11 | |
12 | using namespace llvm; |
13 | |
14 | namespace { |
15 | // This class, beyond being used by the test case, a nice |
16 | // demonstration of the intended usage of TrailingObjects, with a |
17 | // single trailing array. |
18 | class Class1 final : protected TrailingObjects<Class1, short> { |
19 | friend TrailingObjects; |
20 | |
21 | unsigned NumShorts; |
22 | |
23 | protected: |
24 | size_t numTrailingObjects(OverloadToken<short>) const { return NumShorts; } |
25 | |
26 | Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) { |
27 | std::uninitialized_copy(first: ShortArray, last: ShortArray + NumShorts, |
28 | result: getTrailingObjects<short>()); |
29 | } |
30 | |
31 | public: |
32 | static Class1 *create(int *ShortArray, unsigned NumShorts) { |
33 | void *Mem = ::operator new(totalSizeToAlloc<short>(Counts: NumShorts)); |
34 | return new (Mem) Class1(ShortArray, NumShorts); |
35 | } |
36 | void operator delete(void *p) { ::operator delete(p); } |
37 | |
38 | short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; } |
39 | |
40 | unsigned numShorts() const { return NumShorts; } |
41 | |
42 | // Pull some protected members in as public, for testability. |
43 | template <typename... Ty> |
44 | using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>; |
45 | |
46 | using TrailingObjects::totalSizeToAlloc; |
47 | using TrailingObjects::additionalSizeToAlloc; |
48 | using TrailingObjects::getTrailingObjects; |
49 | }; |
50 | |
51 | // Here, there are two singular optional object types appended. Note |
52 | // that the alignment of Class2 is automatically increased to account |
53 | // for the alignment requirements of the trailing objects. |
54 | class Class2 final : protected TrailingObjects<Class2, double, short> { |
55 | friend TrailingObjects; |
56 | |
57 | bool HasShort, HasDouble; |
58 | |
59 | protected: |
60 | size_t numTrailingObjects(OverloadToken<short>) const { |
61 | return HasShort ? 1 : 0; |
62 | } |
63 | size_t numTrailingObjects(OverloadToken<double>) const { |
64 | return HasDouble ? 1 : 0; |
65 | } |
66 | |
67 | Class2(bool HasShort, bool HasDouble) |
68 | : HasShort(HasShort), HasDouble(HasDouble) {} |
69 | |
70 | public: |
71 | static Class2 *create(short S = 0, double D = 0.0) { |
72 | bool HasShort = S != 0; |
73 | bool HasDouble = D != 0.0; |
74 | |
75 | void *Mem = |
76 | ::operator new(totalSizeToAlloc<double, short>(Counts: HasDouble, Counts: HasShort)); |
77 | Class2 *C = new (Mem) Class2(HasShort, HasDouble); |
78 | if (HasShort) |
79 | *C->getTrailingObjects<short>() = S; |
80 | if (HasDouble) |
81 | *C->getTrailingObjects<double>() = D; |
82 | return C; |
83 | } |
84 | void operator delete(void *p) { ::operator delete(p); } |
85 | |
86 | short getShort() const { |
87 | if (!HasShort) |
88 | return 0; |
89 | return *getTrailingObjects<short>(); |
90 | } |
91 | |
92 | double getDouble() const { |
93 | if (!HasDouble) |
94 | return 0.0; |
95 | return *getTrailingObjects<double>(); |
96 | } |
97 | |
98 | // Pull some protected members in as public, for testability. |
99 | template <typename... Ty> |
100 | using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>; |
101 | |
102 | using TrailingObjects::totalSizeToAlloc; |
103 | using TrailingObjects::additionalSizeToAlloc; |
104 | using TrailingObjects::getTrailingObjects; |
105 | }; |
106 | |
107 | TEST(TrailingObjects, OneArg) { |
108 | int arr[] = {1, 2, 3}; |
109 | Class1 *C = Class1::create(ShortArray: arr, NumShorts: 3); |
110 | EXPECT_EQ(sizeof(Class1), sizeof(unsigned)); |
111 | EXPECT_EQ(Class1::additionalSizeToAlloc<short>(1), sizeof(short)); |
112 | EXPECT_EQ(Class1::additionalSizeToAlloc<short>(3), sizeof(short) * 3); |
113 | |
114 | EXPECT_EQ(alignof(Class1), |
115 | alignof(Class1::FixedSizeStorage<short>::with_counts<1>::type)); |
116 | EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<1>::type), |
117 | llvm::alignTo(Class1::totalSizeToAlloc<short>(1), alignof(Class1))); |
118 | EXPECT_EQ(Class1::totalSizeToAlloc<short>(1), sizeof(Class1) + sizeof(short)); |
119 | |
120 | EXPECT_EQ(alignof(Class1), |
121 | alignof(Class1::FixedSizeStorage<short>::with_counts<3>::type)); |
122 | EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<3>::type), |
123 | llvm::alignTo(Class1::totalSizeToAlloc<short>(3), alignof(Class1))); |
124 | EXPECT_EQ(Class1::totalSizeToAlloc<short>(3), |
125 | sizeof(Class1) + sizeof(short) * 3); |
126 | |
127 | EXPECT_EQ(C->getTrailingObjects<short>(), reinterpret_cast<short *>(C + 1)); |
128 | EXPECT_EQ(C->get(0), 1); |
129 | EXPECT_EQ(C->get(2), 3); |
130 | delete C; |
131 | } |
132 | |
133 | TEST(TrailingObjects, TwoArg) { |
134 | Class2 *C1 = Class2::create(S: 4); |
135 | Class2 *C2 = Class2::create(S: 0, D: 4.2); |
136 | |
137 | EXPECT_EQ(sizeof(Class2), llvm::alignTo(sizeof(bool) * 2, alignof(double))); |
138 | EXPECT_EQ(alignof(Class2), alignof(double)); |
139 | |
140 | EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)), |
141 | sizeof(double)); |
142 | EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(0, 1)), |
143 | sizeof(short)); |
144 | EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(3, 1)), |
145 | sizeof(double) * 3 + sizeof(short)); |
146 | |
147 | EXPECT_EQ( |
148 | alignof(Class2), |
149 | (alignof( |
150 | Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type))); |
151 | EXPECT_EQ( |
152 | sizeof(Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type), |
153 | llvm::alignTo(Class2::totalSizeToAlloc<double, short>(1, 1), |
154 | alignof(Class2))); |
155 | EXPECT_EQ((Class2::totalSizeToAlloc<double, short>(1, 1)), |
156 | sizeof(Class2) + sizeof(double) + sizeof(short)); |
157 | |
158 | EXPECT_EQ(C1->getDouble(), 0); |
159 | EXPECT_EQ(C1->getShort(), 4); |
160 | EXPECT_EQ(C1->getTrailingObjects<double>(), |
161 | reinterpret_cast<double *>(C1 + 1)); |
162 | EXPECT_EQ(C1->getTrailingObjects<short>(), reinterpret_cast<short *>(C1 + 1)); |
163 | |
164 | EXPECT_EQ(C2->getDouble(), 4.2); |
165 | EXPECT_EQ(C2->getShort(), 0); |
166 | EXPECT_EQ(C2->getTrailingObjects<double>(), |
167 | reinterpret_cast<double *>(C2 + 1)); |
168 | EXPECT_EQ(C2->getTrailingObjects<short>(), |
169 | reinterpret_cast<short *>(reinterpret_cast<double *>(C2 + 1) + 1)); |
170 | delete C1; |
171 | delete C2; |
172 | } |
173 | |
174 | // This test class is not trying to be a usage demo, just asserting |
175 | // that three args does actually work too (it's the same code as |
176 | // handles the second arg, so it's basically covered by the above, but |
177 | // just in case..) |
178 | class Class3 final : public TrailingObjects<Class3, double, short, bool> { |
179 | friend TrailingObjects; |
180 | |
181 | size_t numTrailingObjects(OverloadToken<double>) const { return 1; } |
182 | size_t numTrailingObjects(OverloadToken<short>) const { return 1; } |
183 | }; |
184 | |
185 | TEST(TrailingObjects, ThreeArg) { |
186 | EXPECT_EQ((Class3::additionalSizeToAlloc<double, short, bool>(1, 1, 3)), |
187 | sizeof(double) + sizeof(short) + 3 * sizeof(bool)); |
188 | EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, alignof(double))); |
189 | |
190 | EXPECT_EQ( |
191 | alignof(Class3), |
192 | (alignof(Class3::FixedSizeStorage<double, short, |
193 | bool>::with_counts<1, 1, 3>::type))); |
194 | EXPECT_EQ( |
195 | sizeof(Class3::FixedSizeStorage<double, short, |
196 | bool>::with_counts<1, 1, 3>::type), |
197 | llvm::alignTo(Class3::totalSizeToAlloc<double, short, bool>(1, 1, 3), |
198 | alignof(Class3))); |
199 | |
200 | std::unique_ptr<char[]> P(new char[1000]); |
201 | Class3 *C = reinterpret_cast<Class3 *>(P.get()); |
202 | EXPECT_EQ(C->getTrailingObjects<double>(), reinterpret_cast<double *>(C + 1)); |
203 | EXPECT_EQ(C->getTrailingObjects<short>(), |
204 | reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1)); |
205 | EXPECT_EQ( |
206 | C->getTrailingObjects<bool>(), |
207 | reinterpret_cast<bool *>( |
208 | reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1) + |
209 | 1)); |
210 | } |
211 | |
212 | class Class4 final : public TrailingObjects<Class4, char, long> { |
213 | friend TrailingObjects; |
214 | size_t numTrailingObjects(OverloadToken<char>) const { return 1; } |
215 | }; |
216 | |
217 | TEST(TrailingObjects, Realignment) { |
218 | EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)), |
219 | llvm::alignTo(sizeof(long) + 1, alignof(long))); |
220 | EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, alignof(long))); |
221 | |
222 | EXPECT_EQ( |
223 | alignof(Class4), |
224 | (alignof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type))); |
225 | EXPECT_EQ( |
226 | sizeof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type), |
227 | llvm::alignTo(Class4::totalSizeToAlloc<char, long>(1, 1), |
228 | alignof(Class4))); |
229 | |
230 | std::unique_ptr<char[]> P(new char[1000]); |
231 | Class4 *C = reinterpret_cast<Class4 *>(P.get()); |
232 | EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1)); |
233 | EXPECT_EQ(C->getTrailingObjects<long>(), |
234 | reinterpret_cast<long *>(llvm::alignAddr( |
235 | reinterpret_cast<char *>(C + 1) + 1, Align::Of<long>()))); |
236 | } |
237 | } |
238 | |
239 | // Test the use of TrailingObjects with a template class. This |
240 | // previously failed to compile due to a bug in MSVC's member access |
241 | // control/lookup handling for OverloadToken. |
242 | template <typename Derived> |
243 | class Class5Tmpl : private llvm::TrailingObjects<Derived, float, int> { |
244 | using TrailingObjects = typename llvm::TrailingObjects<Derived, float>; |
245 | friend TrailingObjects; |
246 | |
247 | size_t numTrailingObjects( |
248 | typename TrailingObjects::template OverloadToken<float>) const { |
249 | return 1; |
250 | } |
251 | |
252 | size_t numTrailingObjects( |
253 | typename TrailingObjects::template OverloadToken<int>) const { |
254 | return 2; |
255 | } |
256 | }; |
257 | |
258 | class Class5 : public Class5Tmpl<Class5> {}; |
259 | |