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:
numTrailingObjects(OverloadToken<short>) const24   size_t numTrailingObjects(OverloadToken<short>) const { return NumShorts; }
25 
Class1(int * ShortArray,unsigned NumShorts)26   Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) {
27     std::uninitialized_copy(ShortArray, ShortArray + NumShorts,
28                             getTrailingObjects<short>());
29   }
30 
31 public:
create(int * ShortArray,unsigned NumShorts)32   static Class1 *create(int *ShortArray, unsigned NumShorts) {
33     void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts));
34     return new (Mem) Class1(ShortArray, NumShorts);
35   }
operator delete(void * p)36   void operator delete(void *p) { ::operator delete(p); }
37 
get(unsigned Num) const38   short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; }
39 
numShorts() const40   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:
numTrailingObjects(OverloadToken<short>) const60   size_t numTrailingObjects(OverloadToken<short>) const {
61     return HasShort ? 1 : 0;
62   }
numTrailingObjects(OverloadToken<double>) const63   size_t numTrailingObjects(OverloadToken<double>) const {
64     return HasDouble ? 1 : 0;
65   }
66 
Class2(bool HasShort,bool HasDouble)67   Class2(bool HasShort, bool HasDouble)
68       : HasShort(HasShort), HasDouble(HasDouble) {}
69 
70 public:
create(short S=0,double D=0.0)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>(HasDouble, 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   }
operator delete(void * p)84   void operator delete(void *p) { ::operator delete(p); }
85 
getShort() const86   short getShort() const {
87     if (!HasShort)
88       return 0;
89     return *getTrailingObjects<short>();
90   }
91 
getDouble() const92   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 
TEST(TrailingObjects,OneArg)107 TEST(TrailingObjects, OneArg) {
108   int arr[] = {1, 2, 3};
109   Class1 *C = Class1::create(arr, 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 
TEST(TrailingObjects,TwoArg)133 TEST(TrailingObjects, TwoArg) {
134   Class2 *C1 = Class2::create(4);
135   Class2 *C2 = Class2::create(0, 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 
numTrailingObjects(OverloadToken<double>) const181   size_t numTrailingObjects(OverloadToken<double>) const { return 1; }
numTrailingObjects(OverloadToken<short>) const182   size_t numTrailingObjects(OverloadToken<short>) const { return 1; }
183 };
184 
TEST(TrailingObjects,ThreeArg)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;
numTrailingObjects(OverloadToken<char>) const214   size_t numTrailingObjects(OverloadToken<char>) const { return 1; }
215 };
216 
TEST(TrailingObjects,Realignment)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 
numTrailingObjects(typename TrailingObjects::template OverloadToken<float>) const247   size_t numTrailingObjects(
248       typename TrailingObjects::template OverloadToken<float>) const {
249     return 1;
250   }
251 
numTrailingObjects(typename TrailingObjects::template OverloadToken<int>) const252   size_t numTrailingObjects(
253       typename TrailingObjects::template OverloadToken<int>) const {
254     return 2;
255   }
256 };
257 
258 class Class5 : public Class5Tmpl<Class5> {};
259