1aa365b2fSJames Y Knight //=== - llvm/unittest/Support/TrailingObjectsTest.cpp ---------------------===//
2aa365b2fSJames Y Knight //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6aa365b2fSJames Y Knight //
7aa365b2fSJames Y Knight //===----------------------------------------------------------------------===//
8aa365b2fSJames Y Knight
9aa365b2fSJames Y Knight #include "llvm/Support/TrailingObjects.h"
10aa365b2fSJames Y Knight #include "gtest/gtest.h"
11aa365b2fSJames Y Knight
12aa365b2fSJames Y Knight using namespace llvm;
13aa365b2fSJames Y Knight
14aa365b2fSJames Y Knight namespace {
15aa365b2fSJames Y Knight // This class, beyond being used by the test case, a nice
16aa365b2fSJames Y Knight // demonstration of the intended usage of TrailingObjects, with a
17aa365b2fSJames Y Knight // single trailing array.
18051eeca0SYaron Keren class Class1 final : protected TrailingObjects<Class1, short> {
19aa365b2fSJames Y Knight friend TrailingObjects;
20aa365b2fSJames Y Knight
21aa365b2fSJames Y Knight unsigned NumShorts;
22aa365b2fSJames Y Knight
23aa365b2fSJames Y Knight protected:
numTrailingObjects(OverloadToken<short>) const24aa365b2fSJames Y Knight size_t numTrailingObjects(OverloadToken<short>) const { return NumShorts; }
25aa365b2fSJames Y Knight
Class1(int * ShortArray,unsigned NumShorts)26aa365b2fSJames Y Knight Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) {
27aa365b2fSJames Y Knight std::uninitialized_copy(ShortArray, ShortArray + NumShorts,
28aa365b2fSJames Y Knight getTrailingObjects<short>());
29aa365b2fSJames Y Knight }
30aa365b2fSJames Y Knight
31aa365b2fSJames Y Knight public:
create(int * ShortArray,unsigned NumShorts)32aa365b2fSJames Y Knight static Class1 *create(int *ShortArray, unsigned NumShorts) {
33aa365b2fSJames Y Knight void *Mem = ::operator new(totalSizeToAlloc<short>(NumShorts));
34aa365b2fSJames Y Knight return new (Mem) Class1(ShortArray, NumShorts);
35aa365b2fSJames Y Knight }
operator delete(void * p)36a64e1adfSRichard Smith void operator delete(void *p) { ::operator delete(p); }
37aa365b2fSJames Y Knight
get(unsigned Num) const38aa365b2fSJames Y Knight short get(unsigned Num) const { return getTrailingObjects<short>()[Num]; }
39aa365b2fSJames Y Knight
numShorts() const40aa365b2fSJames Y Knight unsigned numShorts() const { return NumShorts; }
41aa365b2fSJames Y Knight
42aa365b2fSJames Y Knight // Pull some protected members in as public, for testability.
4301a2cb55SHubert Tong template <typename... Ty>
4401a2cb55SHubert Tong using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>;
4501a2cb55SHubert Tong
46aa365b2fSJames Y Knight using TrailingObjects::totalSizeToAlloc;
47aa365b2fSJames Y Knight using TrailingObjects::additionalSizeToAlloc;
48aa365b2fSJames Y Knight using TrailingObjects::getTrailingObjects;
49aa365b2fSJames Y Knight };
50aa365b2fSJames Y Knight
5164390b42SJames Y Knight // Here, there are two singular optional object types appended. Note
5264390b42SJames Y Knight // that the alignment of Class2 is automatically increased to account
5364390b42SJames Y Knight // for the alignment requirements of the trailing objects.
5464390b42SJames Y Knight class Class2 final : protected TrailingObjects<Class2, double, short> {
55aa365b2fSJames Y Knight friend TrailingObjects;
56aa365b2fSJames Y Knight
57aa365b2fSJames Y Knight bool HasShort, HasDouble;
58aa365b2fSJames Y Knight
59aa365b2fSJames Y Knight protected:
numTrailingObjects(OverloadToken<short>) const60aa365b2fSJames Y Knight size_t numTrailingObjects(OverloadToken<short>) const {
61aa365b2fSJames Y Knight return HasShort ? 1 : 0;
62aa365b2fSJames Y Knight }
numTrailingObjects(OverloadToken<double>) const63aa365b2fSJames Y Knight size_t numTrailingObjects(OverloadToken<double>) const {
64aa365b2fSJames Y Knight return HasDouble ? 1 : 0;
65aa365b2fSJames Y Knight }
66aa365b2fSJames Y Knight
Class2(bool HasShort,bool HasDouble)67aa365b2fSJames Y Knight Class2(bool HasShort, bool HasDouble)
68aa365b2fSJames Y Knight : HasShort(HasShort), HasDouble(HasDouble) {}
69aa365b2fSJames Y Knight
70aa365b2fSJames Y Knight public:
create(short S=0,double D=0.0)71aa365b2fSJames Y Knight static Class2 *create(short S = 0, double D = 0.0) {
72aa365b2fSJames Y Knight bool HasShort = S != 0;
73aa365b2fSJames Y Knight bool HasDouble = D != 0.0;
74aa365b2fSJames Y Knight
75aa365b2fSJames Y Knight void *Mem =
76aa365b2fSJames Y Knight ::operator new(totalSizeToAlloc<double, short>(HasDouble, HasShort));
77aa365b2fSJames Y Knight Class2 *C = new (Mem) Class2(HasShort, HasDouble);
78aa365b2fSJames Y Knight if (HasShort)
79aa365b2fSJames Y Knight *C->getTrailingObjects<short>() = S;
80aa365b2fSJames Y Knight if (HasDouble)
81aa365b2fSJames Y Knight *C->getTrailingObjects<double>() = D;
82aa365b2fSJames Y Knight return C;
83aa365b2fSJames Y Knight }
operator delete(void * p)84a64e1adfSRichard Smith void operator delete(void *p) { ::operator delete(p); }
85aa365b2fSJames Y Knight
getShort() const86aa365b2fSJames Y Knight short getShort() const {
87aa365b2fSJames Y Knight if (!HasShort)
88aa365b2fSJames Y Knight return 0;
89aa365b2fSJames Y Knight return *getTrailingObjects<short>();
90aa365b2fSJames Y Knight }
91aa365b2fSJames Y Knight
getDouble() const92aa365b2fSJames Y Knight double getDouble() const {
93aa365b2fSJames Y Knight if (!HasDouble)
94aa365b2fSJames Y Knight return 0.0;
95aa365b2fSJames Y Knight return *getTrailingObjects<double>();
96aa365b2fSJames Y Knight }
97aa365b2fSJames Y Knight
98aa365b2fSJames Y Knight // Pull some protected members in as public, for testability.
9901a2cb55SHubert Tong template <typename... Ty>
10001a2cb55SHubert Tong using FixedSizeStorage = TrailingObjects::FixedSizeStorage<Ty...>;
10101a2cb55SHubert Tong
102aa365b2fSJames Y Knight using TrailingObjects::totalSizeToAlloc;
103aa365b2fSJames Y Knight using TrailingObjects::additionalSizeToAlloc;
104aa365b2fSJames Y Knight using TrailingObjects::getTrailingObjects;
105aa365b2fSJames Y Knight };
106aa365b2fSJames Y Knight
TEST(TrailingObjects,OneArg)107aa365b2fSJames Y Knight TEST(TrailingObjects, OneArg) {
108aa365b2fSJames Y Knight int arr[] = {1, 2, 3};
109aa365b2fSJames Y Knight Class1 *C = Class1::create(arr, 3);
110aa365b2fSJames Y Knight EXPECT_EQ(sizeof(Class1), sizeof(unsigned));
111aa365b2fSJames Y Knight EXPECT_EQ(Class1::additionalSizeToAlloc<short>(1), sizeof(short));
112aa365b2fSJames Y Knight EXPECT_EQ(Class1::additionalSizeToAlloc<short>(3), sizeof(short) * 3);
113aa365b2fSJames Y Knight
114b2505005SBenjamin Kramer EXPECT_EQ(alignof(Class1),
115b2505005SBenjamin Kramer alignof(Class1::FixedSizeStorage<short>::with_counts<1>::type));
11601a2cb55SHubert Tong EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<1>::type),
117b2505005SBenjamin Kramer llvm::alignTo(Class1::totalSizeToAlloc<short>(1), alignof(Class1)));
118aa365b2fSJames Y Knight EXPECT_EQ(Class1::totalSizeToAlloc<short>(1), sizeof(Class1) + sizeof(short));
11901a2cb55SHubert Tong
120b2505005SBenjamin Kramer EXPECT_EQ(alignof(Class1),
121b2505005SBenjamin Kramer alignof(Class1::FixedSizeStorage<short>::with_counts<3>::type));
12201a2cb55SHubert Tong EXPECT_EQ(sizeof(Class1::FixedSizeStorage<short>::with_counts<3>::type),
123b2505005SBenjamin Kramer llvm::alignTo(Class1::totalSizeToAlloc<short>(3), alignof(Class1)));
124aa365b2fSJames Y Knight EXPECT_EQ(Class1::totalSizeToAlloc<short>(3),
125aa365b2fSJames Y Knight sizeof(Class1) + sizeof(short) * 3);
126aa365b2fSJames Y Knight
127aa365b2fSJames Y Knight EXPECT_EQ(C->getTrailingObjects<short>(), reinterpret_cast<short *>(C + 1));
128aa365b2fSJames Y Knight EXPECT_EQ(C->get(0), 1);
129aa365b2fSJames Y Knight EXPECT_EQ(C->get(2), 3);
130aa365b2fSJames Y Knight delete C;
131aa365b2fSJames Y Knight }
132aa365b2fSJames Y Knight
TEST(TrailingObjects,TwoArg)133aa365b2fSJames Y Knight TEST(TrailingObjects, TwoArg) {
134aa365b2fSJames Y Knight Class2 *C1 = Class2::create(4);
135aa365b2fSJames Y Knight Class2 *C2 = Class2::create(0, 4.2);
136aa365b2fSJames Y Knight
137b2505005SBenjamin Kramer EXPECT_EQ(sizeof(Class2), llvm::alignTo(sizeof(bool) * 2, alignof(double)));
138b2505005SBenjamin Kramer EXPECT_EQ(alignof(Class2), alignof(double));
139aa365b2fSJames Y Knight
140aa365b2fSJames Y Knight EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(1, 0)),
141aa365b2fSJames Y Knight sizeof(double));
142aa365b2fSJames Y Knight EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(0, 1)),
143aa365b2fSJames Y Knight sizeof(short));
144aa365b2fSJames Y Knight EXPECT_EQ((Class2::additionalSizeToAlloc<double, short>(3, 1)),
145aa365b2fSJames Y Knight sizeof(double) * 3 + sizeof(short));
146aa365b2fSJames Y Knight
14701a2cb55SHubert Tong EXPECT_EQ(
148b2505005SBenjamin Kramer alignof(Class2),
149b2505005SBenjamin Kramer (alignof(
150b2505005SBenjamin Kramer Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type)));
15101a2cb55SHubert Tong EXPECT_EQ(
15201a2cb55SHubert Tong sizeof(Class2::FixedSizeStorage<double, short>::with_counts<1, 1>::type),
15301a2cb55SHubert Tong llvm::alignTo(Class2::totalSizeToAlloc<double, short>(1, 1),
154b2505005SBenjamin Kramer alignof(Class2)));
155aa365b2fSJames Y Knight EXPECT_EQ((Class2::totalSizeToAlloc<double, short>(1, 1)),
156aa365b2fSJames Y Knight sizeof(Class2) + sizeof(double) + sizeof(short));
157aa365b2fSJames Y Knight
158aa365b2fSJames Y Knight EXPECT_EQ(C1->getDouble(), 0);
159aa365b2fSJames Y Knight EXPECT_EQ(C1->getShort(), 4);
160aa365b2fSJames Y Knight EXPECT_EQ(C1->getTrailingObjects<double>(),
161aa365b2fSJames Y Knight reinterpret_cast<double *>(C1 + 1));
162aa365b2fSJames Y Knight EXPECT_EQ(C1->getTrailingObjects<short>(), reinterpret_cast<short *>(C1 + 1));
163aa365b2fSJames Y Knight
164aa365b2fSJames Y Knight EXPECT_EQ(C2->getDouble(), 4.2);
165aa365b2fSJames Y Knight EXPECT_EQ(C2->getShort(), 0);
166aa365b2fSJames Y Knight EXPECT_EQ(C2->getTrailingObjects<double>(),
167aa365b2fSJames Y Knight reinterpret_cast<double *>(C2 + 1));
168aa365b2fSJames Y Knight EXPECT_EQ(C2->getTrailingObjects<short>(),
169aa365b2fSJames Y Knight reinterpret_cast<short *>(reinterpret_cast<double *>(C2 + 1) + 1));
170aa365b2fSJames Y Knight delete C1;
171aa365b2fSJames Y Knight delete C2;
172aa365b2fSJames Y Knight }
17364390b42SJames Y Knight
17464390b42SJames Y Knight // This test class is not trying to be a usage demo, just asserting
17564390b42SJames Y Knight // that three args does actually work too (it's the same code as
17664390b42SJames Y Knight // handles the second arg, so it's basically covered by the above, but
17764390b42SJames Y Knight // just in case..)
17864390b42SJames Y Knight class Class3 final : public TrailingObjects<Class3, double, short, bool> {
17964390b42SJames Y Knight friend TrailingObjects;
18064390b42SJames Y Knight
numTrailingObjects(OverloadToken<double>) const18164390b42SJames Y Knight size_t numTrailingObjects(OverloadToken<double>) const { return 1; }
numTrailingObjects(OverloadToken<short>) const18264390b42SJames Y Knight size_t numTrailingObjects(OverloadToken<short>) const { return 1; }
18364390b42SJames Y Knight };
18464390b42SJames Y Knight
TEST(TrailingObjects,ThreeArg)18564390b42SJames Y Knight TEST(TrailingObjects, ThreeArg) {
18664390b42SJames Y Knight EXPECT_EQ((Class3::additionalSizeToAlloc<double, short, bool>(1, 1, 3)),
18764390b42SJames Y Knight sizeof(double) + sizeof(short) + 3 * sizeof(bool));
188b2505005SBenjamin Kramer EXPECT_EQ(sizeof(Class3), llvm::alignTo(1, alignof(double)));
18901a2cb55SHubert Tong
190b2505005SBenjamin Kramer EXPECT_EQ(
191b2505005SBenjamin Kramer alignof(Class3),
192b2505005SBenjamin Kramer (alignof(Class3::FixedSizeStorage<double, short,
193b2505005SBenjamin Kramer bool>::with_counts<1, 1, 3>::type)));
19401a2cb55SHubert Tong EXPECT_EQ(
19501a2cb55SHubert Tong sizeof(Class3::FixedSizeStorage<double, short,
19601a2cb55SHubert Tong bool>::with_counts<1, 1, 3>::type),
19701a2cb55SHubert Tong llvm::alignTo(Class3::totalSizeToAlloc<double, short, bool>(1, 1, 3),
198b2505005SBenjamin Kramer alignof(Class3)));
19901a2cb55SHubert Tong
2009b9dd84cSKostya Serebryany std::unique_ptr<char[]> P(new char[1000]);
2019b9dd84cSKostya Serebryany Class3 *C = reinterpret_cast<Class3 *>(P.get());
20264390b42SJames Y Knight EXPECT_EQ(C->getTrailingObjects<double>(), reinterpret_cast<double *>(C + 1));
20364390b42SJames Y Knight EXPECT_EQ(C->getTrailingObjects<short>(),
20464390b42SJames Y Knight reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1));
20564390b42SJames Y Knight EXPECT_EQ(
20664390b42SJames Y Knight C->getTrailingObjects<bool>(),
20764390b42SJames Y Knight reinterpret_cast<bool *>(
20864390b42SJames Y Knight reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1) +
20964390b42SJames Y Knight 1));
21064390b42SJames Y Knight }
211d734aaa4SJames Y Knight
212d734aaa4SJames Y Knight class Class4 final : public TrailingObjects<Class4, char, long> {
213d734aaa4SJames Y Knight friend TrailingObjects;
numTrailingObjects(OverloadToken<char>) const214d734aaa4SJames Y Knight size_t numTrailingObjects(OverloadToken<char>) const { return 1; }
215d734aaa4SJames Y Knight };
216d734aaa4SJames Y Knight
TEST(TrailingObjects,Realignment)217d734aaa4SJames Y Knight TEST(TrailingObjects, Realignment) {
218d734aaa4SJames Y Knight EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)),
219b2505005SBenjamin Kramer llvm::alignTo(sizeof(long) + 1, alignof(long)));
220b2505005SBenjamin Kramer EXPECT_EQ(sizeof(Class4), llvm::alignTo(1, alignof(long)));
22101a2cb55SHubert Tong
22201a2cb55SHubert Tong EXPECT_EQ(
223b2505005SBenjamin Kramer alignof(Class4),
224b2505005SBenjamin Kramer (alignof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type)));
22501a2cb55SHubert Tong EXPECT_EQ(
22601a2cb55SHubert Tong sizeof(Class4::FixedSizeStorage<char, long>::with_counts<1, 1>::type),
22701a2cb55SHubert Tong llvm::alignTo(Class4::totalSizeToAlloc<char, long>(1, 1),
228b2505005SBenjamin Kramer alignof(Class4)));
22901a2cb55SHubert Tong
230d734aaa4SJames Y Knight std::unique_ptr<char[]> P(new char[1000]);
231d734aaa4SJames Y Knight Class4 *C = reinterpret_cast<Class4 *>(P.get());
232d734aaa4SJames Y Knight EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1));
233d734aaa4SJames Y Knight EXPECT_EQ(C->getTrailingObjects<long>(),
234d734aaa4SJames Y Knight reinterpret_cast<long *>(llvm::alignAddr(
235*ce56e1a1SGuillaume Chatelet reinterpret_cast<char *>(C + 1) + 1, Align::Of<long>())));
236d734aaa4SJames Y Knight }
237aa365b2fSJames Y Knight }
2382fdabb05SJames Y Knight
2392fdabb05SJames Y Knight // Test the use of TrailingObjects with a template class. This
2402fdabb05SJames Y Knight // previously failed to compile due to a bug in MSVC's member access
2412fdabb05SJames Y Knight // control/lookup handling for OverloadToken.
2422fdabb05SJames Y Knight template <typename Derived>
2432fdabb05SJames Y Knight class Class5Tmpl : private llvm::TrailingObjects<Derived, float, int> {
2442fdabb05SJames Y Knight using TrailingObjects = typename llvm::TrailingObjects<Derived, float>;
2452fdabb05SJames Y Knight friend TrailingObjects;
2462fdabb05SJames Y Knight
numTrailingObjects(typename TrailingObjects::template OverloadToken<float>) const2472fdabb05SJames Y Knight size_t numTrailingObjects(
2482fdabb05SJames Y Knight typename TrailingObjects::template OverloadToken<float>) const {
2492fdabb05SJames Y Knight return 1;
2502fdabb05SJames Y Knight }
2512fdabb05SJames Y Knight
numTrailingObjects(typename TrailingObjects::template OverloadToken<int>) const2522fdabb05SJames Y Knight size_t numTrailingObjects(
2532fdabb05SJames Y Knight typename TrailingObjects::template OverloadToken<int>) const {
2542fdabb05SJames Y Knight return 2;
2552fdabb05SJames Y Knight }
2562fdabb05SJames Y Knight };
2572fdabb05SJames Y Knight
2582fdabb05SJames Y Knight class Class5 : public Class5Tmpl<Class5> {};
259