3232
3333namespace {
3434
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.
3983
4084 class AllocatedValue {
4185 public:
4286 typedef void (*DtorFunc_t)(void *);
4387
4488 private:
45- // /\brief The destructor function.
46- DtorFunc_t m_DtorFunc;
4789
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;
5093
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+ };
53100
54101 // /\brief The reference count - once 0, this object will be deallocated.
55102 // / Hopefully 2^55 - 1 references should be enough as the last byte is
56103 // / 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+ };
58112 union {
59113 size_t m_Count;
60114 char m_Bytes[SizeBytes];
61115 };
62116
117+ bool TestFlags (unsigned F) const { return (m_Bytes[FlagsByte] & F) == F; }
118+
63119 size_t UpdateRefCount (int Amount) {
64120 // Bit shift the bytes used in m_Bytes for representing an integer
65121 // respecting endian-ness and which of those bytes are significant.
@@ -73,32 +129,68 @@ namespace {
73129 return RC.m_Count ;
74130 }
75131
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));
79134 }
80135
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) {
86139 m_Count = 0 ;
140+ m_Bytes[FlagsByte] = Info;
87141 UpdateRefCount (1 );
88142 }
89143
90144 public:
91- // /\brief Create an AllocatedValue whose lifetime is reference counted.
145+
146+ // /\brief Create an AllocatedValue.
92147 // / \returns The address of the writeable client data.
93148 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+ }
96163
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" );
97188 static_assert (std::is_standard_layout<AllocatedValue>::value, " padding" );
98189 static_assert (sizeof (m_Count) == sizeof (m_Bytes), " union padding" );
99190 static_assert (((offsetof (AllocatedValue, m_Count) + sizeof (m_Count)) %
100191 SizeBytes) == 0 ,
101192 " Buffer may not be machine aligned" );
193+ // Validate the byte ValueExtractionSynthesizer will write too
102194 assert (&Alloc[sizeof (AllocatedValue) - 1 ] == &AV->m_Bytes [SizeBytes - 1 ]
103195 && " Padded AllocatedValue" );
104196
@@ -115,15 +207,27 @@ namespace {
115207 static void Release (void * Ptr) {
116208 AllocatedValue* AV = FromPtr (Ptr);
117209 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+ }
120217 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);
124220 }
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;
127231 }
128232 }
129233 };
0 commit comments