32
32
33
33
namespace {
34
34
35
- // /\brief The allocation starts with this layout; it is followed by the
36
- // / value's object at m_Payload. This class does not inherit from
37
- // / llvm::RefCountedBase because deallocation cannot use this type but must
38
- // / free the character array.
35
+ // /\brief The layout/usage of memory allocated by AllocatedValue::Create
36
+ // / is dependent on the the type of object it is representing. If the type
37
+ // / has a non-trival destructor then the memory base will point to either
38
+ // / a full Destructable struct (when it is also an array whose size > 1), or
39
+ // / a single DtorFunc_t value when the object is a single instance or an array
40
+ // / with only 1 element.
41
+ // /
42
+ // / If neither of these are true (a POD or array to one), or directly follwing
43
+ // / the prior two cases, is the memory location that can be used to placement
44
+ // / new an AllocatedValue instance.
45
+ // /
46
+ // / The AllocatedValue instance ontains a single union for reference counting
47
+ // / and flags of how the layout exists in memory.
48
+ // /
49
+ // / On 32-bit the reference count will max out a bit before 16.8 million.
50
+ // / 64 bit limit is still extremely high (2^56)-1
51
+ // /
52
+ // /
53
+ // / General layout of memory allocated by AllocatedValue::Create
54
+ // /
55
+ // / +---- Destructable ---+ <- Optional, allocated for arrays
56
+ // / | | TestFlag(kHasElements)
57
+ // / | Size |
58
+ // / | Elements |
59
+ // / | Dtor | <- Can exist without prior Destructable members
60
+ // / | | TestFlag(kHasDestructor)
61
+ // / | |
62
+ // / +---AllocatedValue ---+ <- This object
63
+ // / | | TestFlag(kHasElements)
64
+ // / | union { |
65
+ // / | size_t m_Count |
66
+ // / | char m_Bytes[8] | <- m_Bytes[7] is reserved for AllocatedValue
67
+ // / | }; | & ValueExtractionSynthesizer writing info.
68
+ // / | |
69
+ // / +~~ Client Payload ~~~+ <- Returned from AllocatedValue::Create
70
+ // / | |
71
+ // / | |
72
+ // / +---------------------+
73
+ // /
74
+ // / It may be possible to ignore the caching of this info all together and
75
+ // / just figure out what to do in AllocatedValue::Release by passing the
76
+ // / QualType and Interpreter, but am a bit weary of this for two reasons:
77
+ // /
78
+ // / 1. FIXME: There is still a bad lifetime cycle where a Value referencing
79
+ // / an Interpreter that has been destroyed is possible.
80
+ // / 2. How that might interact with decl unloading, and the possibility of
81
+ // / a destructor no longer being defined after a cling::Value has been
82
+ // / created to represent a fuller state of the type.
39
83
40
84
class AllocatedValue {
41
85
public:
42
86
typedef void (*DtorFunc_t)(void *);
43
87
44
88
private:
45
- // /\brief The destructor function.
46
- DtorFunc_t m_DtorFunc;
47
89
48
- // /\brief The size of the allocation (for arrays)
49
- size_t m_AllocSize;
90
+ struct Destructable {
91
+ // /\brief Size to skip to get the next element in the array.
92
+ size_t Size;
50
93
51
- // /\brief The number of elements in the array
52
- size_t m_NElements;
94
+ // /\brief Total number of elements in the array.
95
+ size_t Elements;
96
+
97
+ // /\brief The destructor function.
98
+ DtorFunc_t Dtor;
99
+ };
53
100
54
101
// /\brief The reference count - once 0, this object will be deallocated.
55
102
// / Hopefully 2^55 - 1 references should be enough as the last byte is
56
103
// / used for flag storage.
57
- enum { SizeBytes = sizeof (size_t ), ConstructedByte = SizeBytes - 1 };
104
+ enum {
105
+ SizeBytes = sizeof (size_t ),
106
+ FlagsByte = SizeBytes - 1 ,
107
+
108
+ kConstructorRan = 1 , // Used by ValueExtractionSynthesizer
109
+ kHasDestructor = 2 ,
110
+ kHasElements = 4
111
+ };
58
112
union {
59
113
size_t m_Count;
60
114
char m_Bytes[SizeBytes];
61
115
};
62
116
117
+ bool TestFlags (unsigned F) const { return (m_Bytes[FlagsByte] & F) == F; }
118
+
63
119
size_t UpdateRefCount (int Amount) {
64
120
// Bit shift the bytes used in m_Bytes for representing an integer
65
121
// respecting endian-ness and which of those bytes are significant.
@@ -73,32 +129,68 @@ namespace {
73
129
return RC.m_Count ;
74
130
}
75
131
76
- static AllocatedValue* FromPtr (void * Ptr) {
77
- return reinterpret_cast <AllocatedValue*>(reinterpret_cast <char *>(Ptr) -
78
- sizeof (AllocatedValue));
132
+ template <class T = AllocatedValue> static T* FromPtr (void * Ptr) {
133
+ return reinterpret_cast <T*>(reinterpret_cast <char *>(Ptr) - sizeof (T));
79
134
}
80
135
81
- // /\brief Initialize the storage management part of the allocated object.
82
- // / The allocator is referencing it, thus initialize m_RefCnt with 1.
83
- // /\param [in] dtorFunc - the function to be called before deallocation.
84
- AllocatedValue (size_t Size, size_t NElem, DtorFunc_t Dtor) :
85
- m_DtorFunc (Dtor), m_AllocSize(Size), m_NElements(NElem) {
136
+ // /\brief Initialize the reference count and flag management.
137
+ // / Everything else is in a Destructable object before -this-
138
+ AllocatedValue (char Info) {
86
139
m_Count = 0 ;
140
+ m_Bytes[FlagsByte] = Info;
87
141
UpdateRefCount (1 );
88
142
}
89
143
90
144
public:
91
- // /\brief Create an AllocatedValue whose lifetime is reference counted.
145
+
146
+ // /\brief Create an AllocatedValue.
92
147
// / \returns The address of the writeable client data.
93
148
static void * Create (size_t Size, size_t NElem, DtorFunc_t Dtor) {
94
- char * Alloc = new char [sizeof (AllocatedValue) + Size];
95
- AllocatedValue* AV = new (Alloc) AllocatedValue (Size, NElem, Dtor);
149
+ size_t AllocSize = sizeof (AllocatedValue) + Size;
150
+ size_t ExtraSize = 0 ;
151
+ char Flags = 0 ;
152
+ if (Dtor) {
153
+ // Only need the elements data for arrays larger than 1.
154
+ if (NElem > 1 ) {
155
+ Flags |= kHasElements ;
156
+ ExtraSize = sizeof (Destructable);
157
+ } else
158
+ ExtraSize = sizeof (DtorFunc_t);
159
+
160
+ Flags |= kHasDestructor ;
161
+ AllocSize += ExtraSize;
162
+ }
96
163
164
+ char * Alloc = new char [AllocSize];
165
+
166
+ if (Dtor) {
167
+ // Move the Buffer ptr to where AllocatedValue begins
168
+ Alloc += ExtraSize;
169
+ // Now back up to get the location of the Destructable members
170
+ // This is so writing to Destructable::Dtor will work when only
171
+ // additional space for DtorFunc_t was written.
172
+ Destructable* DS = FromPtr<Destructable>(Alloc);
173
+ if (NElem > 1 ) {
174
+ DS->Elements = NElem;
175
+ // Hopefully there won't be any issues with object alignemnt of arrays
176
+ // If there are, that would have to be dealt with here and write the
177
+ // proper skip amount in DS->Size.
178
+ DS->Size = Size / NElem;
179
+ }
180
+ DS->Dtor = Dtor;
181
+ }
182
+
183
+ AllocatedValue* AV = new (Alloc) AllocatedValue (Flags);
184
+
185
+ // Just make sure alignment is as expected.
186
+ static_assert (std::is_standard_layout<Destructable>::value, " padding" );
187
+ static_assert ((sizeof (Destructable) % SizeBytes) == 0 , " alignment" );
97
188
static_assert (std::is_standard_layout<AllocatedValue>::value, " padding" );
98
189
static_assert (sizeof (m_Count) == sizeof (m_Bytes), " union padding" );
99
190
static_assert (((offsetof (AllocatedValue, m_Count) + sizeof (m_Count)) %
100
191
SizeBytes) == 0 ,
101
192
" Buffer may not be machine aligned" );
193
+ // Validate the byte ValueExtractionSynthesizer will write too
102
194
assert (&Alloc[sizeof (AllocatedValue) - 1 ] == &AV->m_Bytes [SizeBytes - 1 ]
103
195
&& " Padded AllocatedValue" );
104
196
@@ -115,15 +207,27 @@ namespace {
115
207
static void Release (void * Ptr) {
116
208
AllocatedValue* AV = FromPtr (Ptr);
117
209
if (AV->UpdateRefCount (-1 ) == 0 ) {
118
- if (AV->m_DtorFunc && AV->m_Bytes [ConstructedByte]) {
119
- assert (AV->m_NElements && " No elements!" );
210
+ if (AV->TestFlags (kConstructorRan |kHasDestructor )) {
211
+ Destructable* Dtor = FromPtr<Destructable>(AV);
212
+ size_t Elements = 1 , Size = 0 ;
213
+ if (AV->TestFlags (kHasElements )) {
214
+ Elements = Dtor->Elements ;
215
+ Size = Dtor->Size ;
216
+ }
120
217
char * Payload = reinterpret_cast <char *>(Ptr);
121
- const auto Skip = AV->m_AllocSize / AV->m_NElements ;
122
- while (AV->m_NElements -- != 0 )
123
- (*AV->m_DtorFunc )(Payload + AV->m_NElements * Skip);
218
+ while (Elements-- != 0 )
219
+ (*Dtor->Dtor )(Payload + Elements * Size);
124
220
}
125
- this ->~AllocatedValue ();
126
- delete [] reinterpret_cast <char *>(AV);
221
+
222
+ // Subtract the amount that was over-allocated from the base of -this-
223
+ char * Allocated = reinterpret_cast <char *>(AV);
224
+ if (AV->TestFlags (kHasElements ))
225
+ Allocated -= sizeof (Destructable);
226
+ else if (AV->TestFlags (kHasDestructor ))
227
+ Allocated -= sizeof (DtorFunc_t);
228
+
229
+ AV->~AllocatedValue ();
230
+ delete [] Allocated;
127
231
}
128
232
}
129
233
};
0 commit comments