aarch64: Fix sve/acle/general/ldff1_8.c failures
[gcc.git] / libphobos / libdruntime / core / internal / gc / blockmeta.d
blob768dcd76ba3041e99acdb0fac620cdd46f43189d
1 /**
2 Functions to manipulate metadata in-block.
4 functionality was moved from rt.lifetime
5 */
6 module core.internal.gc.blockmeta;
8 import core.memory;
10 alias BlkInfo = GC.BlkInfo;
11 alias BlkAttr = GC.BlkAttr;
13 enum : size_t
15 PAGESIZE = 4096,
16 BIGLENGTHMASK = ~(PAGESIZE - 1),
17 SMALLPAD = 1,
18 MEDPAD = ushort.sizeof,
19 LARGEPREFIX = 16, // 16 bytes padding at the front of the array
20 LARGEPAD = LARGEPREFIX + 1,
21 MAXSMALLSIZE = 256-SMALLPAD,
22 MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD
25 // size used to store the TypeInfo at the end of an allocation for structs that have a destructor
26 size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
28 if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
30 auto sti = cast(TypeInfo_Struct)cast(void*)ti;
31 if (sti.xdtor)
32 return size_t.sizeof;
34 return 0;
37 /**
38 Set the allocated length of the array block. This is called
39 any time an array is appended to or its length is set.
41 The allocated block looks like this for blocks < PAGESIZE:
43 |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|
46 The size of the allocated length at the end depends on the block size:
48 a block of 16 to 256 bytes has an 8-bit length.
50 a block with 512 to pagesize/2 bytes has a 16-bit length.
52 For blocks >= pagesize, the length is a size_t and is at the beginning of the
53 block. The reason we have to do this is because the block can extend into
54 more pages, so we cannot trust the block length if it sits at the end of the
55 block, because it might have just been extended. If we can prove in the
56 future that the block is unshared, we may be able to change this, but I'm not
57 sure it's important.
59 In order to do put the length at the front, we have to provide 16 bytes
60 buffer space in case the block has to be aligned properly. In x86, certain
61 SSE instructions will only work if the data is 16-byte aligned. In addition,
62 we need the sentinel byte to prevent accidental pointers to the next block.
63 Because of the extra overhead, we only do this for page size and above, where
64 the overhead is minimal compared to the block size.
66 So for those blocks, it looks like:
68 |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|
70 where elem0 starts 16 bytes after the first byte.
72 bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = size_t.max) pure nothrow
74 __setBlockFinalizerInfo(info, tinext);
76 size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
77 return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize);
80 // the impl function, used both above and in core.internal.array.utils
81 bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow
83 import core.atomic;
85 if (info.size <= 256)
87 import core.checkedint;
89 bool overflow;
90 auto newlength_padded = addu(newlength,
91 addu(SMALLPAD, typeInfoSize, overflow),
92 overflow);
94 if (newlength_padded > info.size || overflow)
95 // new size does not fit inside block
96 return false;
98 auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
99 if (oldlength != size_t.max)
101 if (isshared)
103 return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength);
105 else
107 if (*length == cast(ubyte)oldlength)
108 *length = cast(ubyte)newlength;
109 else
110 return false;
113 else
115 // setting the initial length, no cas needed
116 *length = cast(ubyte)newlength;
119 else if (info.size < PAGESIZE)
121 if (newlength + MEDPAD + typeInfoSize > info.size)
122 // new size does not fit inside block
123 return false;
124 auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
125 if (oldlength != size_t.max)
127 if (isshared)
129 return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength);
131 else
133 if (*length == oldlength)
134 *length = cast(ushort)newlength;
135 else
136 return false;
139 else
141 // setting the initial length, no cas needed
142 *length = cast(ushort)newlength;
145 else
147 if (newlength + LARGEPAD > info.size)
148 // new size does not fit inside block
149 return false;
150 auto length = cast(size_t *)(info.base);
151 if (oldlength != size_t.max)
153 if (isshared)
155 return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength);
157 else
159 if (*length == oldlength)
160 *length = newlength;
161 else
162 return false;
165 else
167 // setting the initial length, no cas needed
168 *length = newlength;
171 return true; // resize succeeded
175 The block finalizer info is set separately from the array length, as that is
176 only needed on the initial setup of the block. No shared is needed, since
177 this should only happen when the block is new.
178 If the STRUCTFINAL bit is not set, no finalizer is stored (but if needed the
179 slot is zeroed)
181 void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow
183 if ((info.attr & BlkAttr.APPENDABLE) && info.size >= PAGESIZE)
185 // if the structfinal bit is not set, we don't have a finalizer. But we
186 // should still zero out the finalizer slot.
187 auto context = (info.attr & BlkAttr.STRUCTFINAL) ? cast(void*)ti : null;
189 // array used size goes at the beginning. We can stuff the typeinfo
190 // right after it, as we need to use 16 bytes anyway.
192 auto typeInfo = cast(void**)info.base + 1;
193 *typeInfo = context;
194 version (D_LP64) {} else
196 // zero out the extra padding
197 (cast(size_t*)info.base)[2 .. 4] = 0;
200 else if(info.attr & BlkAttr.STRUCTFINAL)
202 // all other cases the typeinfo gets put at the end of the block
203 auto typeInfo = cast(void**)(info.base + info.size) - 1;
204 *typeInfo = cast(void*) ti;
209 Get the finalizer info from the block (typeinfo).
210 Must be called on a block with STRUCTFINAL set.
212 const(TypeInfo) __getBlockFinalizerInfo(ref BlkInfo info) pure nothrow
214 bool isLargeArray = (info.attr & BlkAttr.APPENDABLE) && info.size >= PAGESIZE;
215 auto typeInfo = isLargeArray ?
216 info.base + size_t.sizeof :
217 info.base + info.size - size_t.sizeof;
218 return *cast(TypeInfo*)typeInfo;
222 get the used size of the array for the given block
224 size_t __arrayAllocLength(ref BlkInfo info) pure nothrow
225 in(info.attr & BlkAttr.APPENDABLE)
227 auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
228 if (info.size <= 256)
229 return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
231 if (info.size < PAGESIZE)
232 return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
234 return *cast(size_t *)(info.base);
238 Atomically get the used size of the array for the given block
240 size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow
241 in(info.attr & BlkAttr.APPENDABLE)
243 import core.atomic;
244 auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
245 if (info.size <= 256)
246 return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD));
248 if (info.size < PAGESIZE)
249 return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD));
251 return atomicLoad(*cast(shared(size_t)*)(info.base));
255 Get the maximum bytes that can be stored in the given block.
257 size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow
258 in(info.attr & BlkAttr.APPENDABLE)
260 // Capacity is a calculation based solely on the block info.
261 if (info.size >= PAGESIZE)
262 return info.size - LARGEPAD;
264 auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
265 auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD;
266 return info.size - typeInfoSize - padsize;
270 get the padding required to allocate size bytes. Note that the padding is
271 NOT included in the passed in size. Therefore, do NOT call this function
272 with the size of an allocated block.
274 size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
276 return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
280 get the padding required to allocate size bytes, use the bits to determine
281 which metadata must be stored.
283 size_t __allocPad(size_t size, uint bits) nothrow pure @trusted
285 auto finalizerSize = (bits & BlkAttr.STRUCTFINAL) ? (void*).sizeof : 0;
286 if (bits & BlkAttr.APPENDABLE)
288 if (size > MAXMEDSIZE - finalizerSize)
289 return LARGEPAD;
290 auto pad = (size > MAXSMALLSIZE - finalizerSize) ? MEDPAD : SMALLPAD;
291 return pad + finalizerSize;
294 return finalizerSize;
298 * Get the start of the array for the given block.
300 * Params:
301 * info = array metadata
302 * Returns:
303 * pointer to the start of the array
305 void *__arrayStart()(return scope BlkInfo info) nothrow pure
307 return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0);