llvm.org GIT mirror llvm / 1cf6cc7
Add a TrailingObjects template class. This is intended to help support the idiom of a class that has some other objects (or multiple arrays of different types of objects) appended on the end, which is used quite heavily in clang. Differential Revision: http://reviews.llvm.org/D11272 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@244164 91177308-0d34-0410-b5e6-96231b3b80d8 James Y Knight 5 years ago
6 changed file(s) with 412 addition(s) and 26 deletion(s). Raw diff Collapse all Expand all
0 //===--- TrailingObjects.h - Variable-length classes ------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This header defines support for implementing classes that have
11 /// some trailing object (or arrays of objects) appended to them. The
12 /// main purpose is to make it obvious where this idiom is being used,
13 /// and to make the usage more idiomatic and more difficult to get
14 /// wrong.
15 ///
16 /// The TrailingObject template abstracts away the reinterpret_cast,
17 /// pointer arithmetic, and size calculations used for the allocation
18 /// and access of appended arrays of objects, as well as asserts that
19 /// the alignment of the classes involved are appropriate for the
20 /// usage. Additionally, it ensures that the base type is final --
21 /// deriving from a class that expects data appended immediately after
22 /// it is typically not safe.
23 ///
24 /// Users are expected to derive from this template, and provide
25 /// numTrailingObjects implementations for each trailing type,
26 /// e.g. like this sample:
27 ///
28 /// \code
29 /// class VarLengthObj : private TrailingObjects {
30 /// friend TrailingObjects;
31 ///
32 /// unsigned NumInts, NumDoubles;
33 /// size_t numTrailingObjects(OverloadToken) const { return NumInts; }
34 /// size_t numTrailingObjects(OverloadToken) const {
35 /// return NumDoubles;
36 /// }
37 /// };
38 /// \endcode
39 ///
40 /// You can access the appended arrays via 'getTrailingObjects', and
41 /// determine the size needed for allocation via
42 /// 'additionalSizeToAlloc' and 'totalSizeToAlloc'.
43 ///
44 /// All the methods implemented by this class are are intended for use
45 /// by the implementation of the class, not as part of its interface
46 /// (thus, private inheritance is suggested).
47 ///
48 //===----------------------------------------------------------------------===//
49
50 #ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H
51 #define LLVM_SUPPORT_TRAILINGOBJECTS_H
52
53 #include
54 #include
55 #include "llvm/Support/AlignOf.h"
56 #include "llvm/Support/Compiler.h"
57 #include "llvm/Support/type_traits.h"
58
59 namespace llvm {
60
61 /// The base class for TrailingObjects* classes.
62 class TrailingObjectsBase {
63 protected:
64 /// OverloadToken's purpose is to allow specifying function overloads
65 /// for different types, without actually taking the types as
66 /// parameters. (Necessary because member function templates cannot
67 /// be specialized, so overloads must be used instead of
68 /// specialization.)
69 template struct OverloadToken {};
70 };
71
72 // Internally used to indicate that the user didn't supply this value,
73 // so the explicit-specialization for fewer args will be used.
74 class NoTrailingTypeArg {};
75
76 // TODO: Consider using a single variadic implementation instead of
77 // multiple copies of the TrailingObjects template? [but, variadic
78 // template recursive implementations are annoying...]
79
80 /// This is the two-type version of the TrailingObjects template; see
81 /// file docstring for details.
82 template
83 typename TrailingTy2 = NoTrailingTypeArg>
84 class TrailingObjects : public TrailingObjectsBase {
85 private:
86 // Contains static_assert statements for the alignment of the
87 // types. Must not be at class-level, because BaseTy isn't complete
88 // at class instantiation time, but will be by the time this
89 // function is instantiated.
90 static void verifyTrailingObjectsAssertions() {
91 static_assert(llvm::AlignOf::Alignment >=
92 llvm::AlignOf::Alignment,
93 "TrailingTy1 requires more alignment than BaseTy provides");
94 static_assert(
95 llvm::AlignOf::Alignment >=
96 llvm::AlignOf::Alignment,
97 "TrailingTy2 requires more alignment than TrailingTy1 provides");
98
99 #ifdef LLVM_IS_FINAL
100 static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
101 #endif
102 }
103
104 // The next four functions are internal helpers for getTrailingObjects.
105 static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
106 OverloadToken) {
107 return reinterpret_cast(Obj + 1);
108 }
109
110 static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
111 OverloadToken) {
112 return reinterpret_cast(Obj + 1);
113 }
114
115 static const TrailingTy2 *getTrailingObjectsImpl(const BaseTy *Obj,
116 OverloadToken) {
117 return reinterpret_cast(
118 getTrailingObjectsImpl(Obj, OverloadToken()) +
119 Obj->numTrailingObjects(OverloadToken()));
120 }
121
122 static TrailingTy2 *getTrailingObjectsImpl(BaseTy *Obj,
123 OverloadToken) {
124 return reinterpret_cast(
125 getTrailingObjectsImpl(Obj, OverloadToken()) +
126 Obj->numTrailingObjects(OverloadToken()));
127 }
128
129 protected:
130 /// Returns a pointer to the trailing object array of the given type
131 /// (which must be one of those specified in the class template). The
132 /// array may have zero or more elements in it.
133 template const T *getTrailingObjects() const {
134 verifyTrailingObjectsAssertions();
135 // Forwards to an impl function with overloads, since member
136 // function templates can't be specialized.
137 return getTrailingObjectsImpl(static_cast(this),
138 OverloadToken());
139 }
140
141 /// Returns a pointer to the trailing object array of the given type
142 /// (which must be one of those specified in the class template). The
143 /// array may have zero or more elements in it.
144 template T *getTrailingObjects() {
145 verifyTrailingObjectsAssertions();
146 // Forwards to an impl function with overloads, since member
147 // function templates can't be specialized.
148 return getTrailingObjectsImpl(static_cast(this),
149 OverloadToken());
150 }
151
152 /// Returns the size of the trailing data, if an object were
153 /// allocated with the given counts (The counts are in the same order
154 /// as the template arguments). This does not include the size of the
155 /// base object. The template arguments must be the same as those
156 /// used in the class; they are supplied here redundantly only so
157 /// that it's clear what the counts are counting in callers.
158 template
159 typename std::enable_if::value &&
160 std::is_same::value,
161 int>::type = 0>
162 static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1, size_t Count2) {
163 return sizeof(TrailingTy1) * Count1 + sizeof(TrailingTy2) * Count2;
164 }
165
166 /// Returns the total size of an object if it were allocated with the
167 /// given trailing object counts. This is the same as
168 /// additionalSizeToAlloc, except it *does* include the size of the base
169 /// object.
170 template
171 static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1, size_t Count2) {
172 return sizeof(BaseTy) + additionalSizeToAlloc(Count1, Count2);
173 }
174 };
175
176 /// This is the one-type version of the TrailingObjects template. See
177 /// the two-type version for more documentation.
178 template
179 class TrailingObjects
180 : public TrailingObjectsBase {
181 private:
182 static void verifyTrailingObjectsAssertions() {
183 static_assert(llvm::AlignOf::Alignment >=
184 llvm::AlignOf::Alignment,
185 "TrailingTy1 requires more alignment than BaseTy provides");
186
187 #ifdef LLVM_IS_FINAL
188 static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
189 #endif
190 }
191
192 static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
193 OverloadToken) {
194 return reinterpret_cast(Obj + 1);
195 }
196
197 static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
198 OverloadToken) {
199 return reinterpret_cast(Obj + 1);
200 }
201
202 protected:
203 template const T *getTrailingObjects() const {
204 verifyTrailingObjectsAssertions();
205 return getTrailingObjectsImpl(static_cast(this),
206 OverloadToken());
207 }
208
209 template T *getTrailingObjects() {
210 verifyTrailingObjectsAssertions();
211 return getTrailingObjectsImpl(static_cast(this),
212 OverloadToken());
213 }
214
215 template
216 typename std::enable_if::value,
217 int>::type = 0>
218 static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1) {
219 return sizeof(TrailingTy1) * Count1;
220 }
221
222 template
223 static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1) {
224 return sizeof(BaseTy) + additionalSizeToAlloc(Count1);
225 }
226 };
227
228 } // end namespace llvm
229
230 #endif
9292
9393 }
9494
95 // If the compiler supports detecting whether a class is final, define
96 // an LLVM_IS_FINAL macro. If it cannot be defined properly, this
97 // macro will be left undefined.
98 #if __cplusplus >= 201402L
99 #define LLVM_IS_FINAL(Ty) std::is_final()
100 #elif __has_feature(is_final) || LLVM_GNUC_PREREQ(4, 7, 0)
101 #define LLVM_IS_FINAL(Ty) __is_final(Ty)
102 #endif
103
95104 #ifdef LLVM_DEFINED_HAS_FEATURE
96105 #undef __has_feature
97106 #endif
1717
1818 #include "llvm/ADT/FoldingSet.h"
1919 #include "llvm/IR/Attributes.h"
20 #include "llvm/Support/TrailingObjects.h"
2021 #include
2122
2223 namespace llvm {
140141 /// \class
141142 /// \brief This class represents a group of attributes that apply to one
142143 /// element: function, return type, or parameter.
143 class AttributeSetNode : public FoldingSetNode {
144 class AttributeSetNode final
145 : public FoldingSetNode,
146 private TrailingObjects {
147 friend TrailingObjects;
148
144149 unsigned NumAttrs; ///< Number of attributes in this node.
145150
146151 AttributeSetNode(ArrayRef Attrs) : NumAttrs(Attrs.size()) {
147152 // There's memory after the node where we can store the entries in.
148 std::copy(Attrs.begin(), Attrs.end(),
149 reinterpret_cast(this + 1));
153 std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects());
150154 }
151155
152156 // AttributesSetNode is uniqued, these should not be publicly available.
169173 std::string getAsString(bool InAttrGrp) const;
170174
171175 typedef const Attribute *iterator;
172 iterator begin() const { return reinterpret_cast(this + 1); }
176 iterator begin() const { return getTrailingObjects(); }
173177 iterator end() const { return begin() + NumAttrs; }
174178
175179 void Profile(FoldingSetNodeID &ID) const {
180184 AttrList[I].Profile(ID);
181185 }
182186 };
183 static_assert(
184 AlignOf::Alignment >= AlignOf::Alignment,
185 "Alignment is insufficient for objects appended to AttributeSetNode");
186187
187188 //===----------------------------------------------------------------------===//
188189 /// \class
189190 /// \brief This class represents a set of attributes that apply to the function,
190191 /// return type, and parameters.
191 class AttributeSetImpl : public FoldingSetNode {
192 typedef std::pair IndexAttrPair;
193
194 class AttributeSetImpl final
195 : public FoldingSetNode,
196 private TrailingObjects {
192197 friend class AttributeSet;
193
194 public:
195 typedef std::pair IndexAttrPair;
198 friend TrailingObjects;
196199
197200 private:
198201 LLVMContext &Context;
199202 unsigned NumAttrs; ///< Number of entries in this set.
200203
204 // Helper fn for TrailingObjects class.
205 size_t numTrailingObjects(OverloadToken) { return NumAttrs; }
206
201207 /// \brief Return a pointer to the IndexAttrPair for the specified slot.
202208 const IndexAttrPair *getNode(unsigned Slot) const {
203 return reinterpret_cast(this + 1) + Slot;
209 return getTrailingObjects() + Slot;
204210 }
205211
206212 // AttributesSet is uniqued, these should not be publicly available.
221227 }
222228 #endif
223229 // There's memory after the node where we can store the entries in.
224 std::copy(Attrs.begin(), Attrs.end(),
225 reinterpret_cast(this + 1));
230 std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects());
226231 }
227232
228233 /// \brief Get the context that created this AttributeSetImpl.
272277
273278 void dump() const;
274279 };
275 static_assert(
276 AlignOf::Alignment >=
277 AlignOf::Alignment,
278 "Alignment is insufficient for objects appended to AttributeSetImpl");
279280
280281 } // end llvm namespace
281282
483483 // new one and insert it.
484484 if (!PA) {
485485 // Coallocate entries after the AttributeSetNode itself.
486 void *Mem = ::operator new(sizeof(AttributeSetNode) +
487 sizeof(Attribute) * SortedAttrs.size());
486 void *Mem = ::operator new(totalSizeToAlloc(SortedAttrs.size()));
488487 PA = new (Mem) AttributeSetNode(SortedAttrs);
489488 pImpl->AttrsSetNodes.InsertNode(PA, InsertPoint);
490489 }
616615 // create a new one and insert it.
617616 if (!PA) {
618617 // Coallocate entries after the AttributeSetImpl itself.
619 void *Mem = ::operator new(sizeof(AttributeSetImpl) +
620 sizeof(std::pair) *
621 Attrs.size());
618 void *Mem = ::operator new(
619 AttributeSetImpl::totalSizeToAlloc(Attrs.size()));
622620 PA = new (Mem) AttributeSetImpl(C, Attrs);
623621 pImpl->AttrsLists.InsertNode(PA, InsertPoint);
624622 }
735733 if (!AS) continue;
736734 SmallVector, 8>::iterator
737735 ANVI = AttrNodeVec.begin(), ANVE;
738 for (const AttributeSetImpl::IndexAttrPair
739 *AI = AS->getNode(0),
740 *AE = AS->getNode(AS->getNumAttributes());
736 for (const IndexAttrPair *AI = AS->getNode(0),
737 *AE = AS->getNode(AS->getNumAttributes());
741738 AI != AE; ++AI) {
742739 ANVE = AttrNodeVec.end();
743740 while (ANVI != ANVE && ANVI->first <= AI->first)
4040 TargetRegistry.cpp
4141 ThreadLocalTest.cpp
4242 TimeValueTest.cpp
43 TrailingObjectsTest.cpp
4344 UnicodeTest.cpp
4445 YAMLIOTest.cpp
4546 YAMLParserTest.cpp
0 //=== - llvm/unittest/Support/TrailingObjectsTest.cpp ---------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
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 : private TrailingObjects {
19 friend TrailingObjects;
20
21 unsigned NumShorts;
22
23 protected:
24 size_t numTrailingObjects(OverloadToken) const { return NumShorts; }
25
26 Class1(int *ShortArray, unsigned NumShorts) : NumShorts(NumShorts) {
27 std::uninitialized_copy(ShortArray, ShortArray + NumShorts,
28 getTrailingObjects());
29 }
30
31 public:
32 static Class1 *create(int *ShortArray, unsigned NumShorts) {
33 void *Mem = ::operator new(totalSizeToAlloc(NumShorts));
34 return new (Mem) Class1(ShortArray, NumShorts);
35 }
36
37 short get(unsigned Num) const { return getTrailingObjects()[Num]; }
38
39 unsigned numShorts() const { return NumShorts; }
40
41 // Pull some protected members in as public, for testability.
42 using TrailingObjects::totalSizeToAlloc;
43 using TrailingObjects::additionalSizeToAlloc;
44 using TrailingObjects::getTrailingObjects;
45 };
46
47 // Here, there are two singular optional object types appended.
48 // Note that it fails to compile without the alignment spec.
49 class LLVM_ALIGNAS(8) Class2 final : private TrailingObjects {
50 friend TrailingObjects;
51
52 bool HasShort, HasDouble;
53
54 protected:
55 size_t numTrailingObjects(OverloadToken) const {
56 return HasShort ? 1 : 0;
57 }
58 size_t numTrailingObjects(OverloadToken) const {
59 return HasDouble ? 1 : 0;
60 }
61
62 Class2(bool HasShort, bool HasDouble)
63 : HasShort(HasShort), HasDouble(HasDouble) {}
64
65 public:
66 static Class2 *create(short S = 0, double D = 0.0) {
67 bool HasShort = S != 0;
68 bool HasDouble = D != 0.0;
69
70 void *Mem =
71 ::operator new(totalSizeToAlloc(HasDouble, HasShort));
72 Class2 *C = new (Mem) Class2(HasShort, HasDouble);
73 if (HasShort)
74 *C->getTrailingObjects() = S;
75 if (HasDouble)
76 *C->getTrailingObjects() = D;
77 return C;
78 }
79
80 short getShort() const {
81 if (!HasShort)
82 return 0;
83 return *getTrailingObjects();
84 }
85
86 double getDouble() const {
87 if (!HasDouble)
88 return 0.0;
89 return *getTrailingObjects();
90 }
91
92 // Pull some protected members in as public, for testability.
93 using TrailingObjects::totalSizeToAlloc;
94 using TrailingObjects::additionalSizeToAlloc;
95 using TrailingObjects::getTrailingObjects;
96 };
97
98 TEST(TrailingObjects, OneArg) {
99 int arr[] = {1, 2, 3};
100 Class1 *C = Class1::create(arr, 3);
101 EXPECT_EQ(sizeof(Class1), sizeof(unsigned));
102 EXPECT_EQ(Class1::additionalSizeToAlloc(1), sizeof(short));
103 EXPECT_EQ(Class1::additionalSizeToAlloc(3), sizeof(short) * 3);
104
105 EXPECT_EQ(Class1::totalSizeToAlloc(1), sizeof(Class1) + sizeof(short));
106 EXPECT_EQ(Class1::totalSizeToAlloc(3),
107 sizeof(Class1) + sizeof(short) * 3);
108
109 EXPECT_EQ(C->getTrailingObjects(), reinterpret_cast(C + 1));
110 EXPECT_EQ(C->get(0), 1);
111 EXPECT_EQ(C->get(2), 3);
112 delete C;
113 }
114
115 TEST(TrailingObjects, TwoArg) {
116 Class2 *C1 = Class2::create(4);
117 Class2 *C2 = Class2::create(0, 4.2);
118
119 EXPECT_EQ(sizeof(Class2), 8u); // due to alignment
120
121 EXPECT_EQ((Class2::additionalSizeToAlloc(1, 0)),
122 sizeof(double));
123 EXPECT_EQ((Class2::additionalSizeToAlloc(0, 1)),
124 sizeof(short));
125 EXPECT_EQ((Class2::additionalSizeToAlloc(3, 1)),
126 sizeof(double) * 3 + sizeof(short));
127
128 EXPECT_EQ((Class2::totalSizeToAlloc(1, 1)),
129 sizeof(Class2) + sizeof(double) + sizeof(short));
130
131 EXPECT_EQ(C1->getDouble(), 0);
132 EXPECT_EQ(C1->getShort(), 4);
133 EXPECT_EQ(C1->getTrailingObjects(),
134 reinterpret_cast(C1 + 1));
135 EXPECT_EQ(C1->getTrailingObjects(), reinterpret_cast(C1 + 1));
136
137 EXPECT_EQ(C2->getDouble(), 4.2);
138 EXPECT_EQ(C2->getShort(), 0);
139 EXPECT_EQ(C2->getTrailingObjects(),
140 reinterpret_cast(C2 + 1));
141 EXPECT_EQ(C2->getTrailingObjects(),
142 reinterpret_cast(reinterpret_cast(C2 + 1) + 1));
143 delete C1;
144 delete C2;
145 }
146 }