master
1/*
2 * Copyright 2018 The Emscripten Authors. All rights reserved.
3 * Emscripten is available under two separate licenses, the MIT license and the
4 * University of Illinois/NCSA Open Source License. Both these licenses can be
5 * found in the LICENSE file.
6 *
7 * Simple minimalistic but efficient sbrk()-based malloc/free that works in
8 * singlethreaded and multithreaded builds.
9 *
10 * Assumptions:
11 *
12 * - sbrk() is used to claim new memory (sbrk handles geometric/linear
13 * - overallocation growth)
14 * - sbrk() can be used by other code outside emmalloc.
15 * - sbrk() is very fast in most cases (internal wasm call).
16 * - sbrk() returns pointers with an alignment of alignof(max_align_t)
17 *
18 * Invariants:
19 *
20 * - Per-allocation header overhead is 8 bytes, smallest allocated payload
21 * amount is 8 bytes, and a multiple of 4 bytes.
22 * - Acquired memory blocks are subdivided into disjoint regions that lie
23 * next to each other.
24 * - A region is either in used or free.
25 * Used regions may be adjacent, and a used and unused region
26 * may be adjacent, but not two unused ones - they would be
27 * merged.
28 * - Memory allocation takes constant time, unless the alloc needs to sbrk()
29 * or memory is very close to being exhausted.
30 *
31 * Debugging:
32 *
33 * - If not NDEBUG, runtime assert()s are in use.
34 * - If EMMALLOC_MEMVALIDATE is defined, a large amount of extra checks are done.
35 * - If EMMALLOC_VERBOSE is defined, a lot of operations are logged
36 * out, in addition to EMMALLOC_MEMVALIDATE.
37 * - Debugging and logging directly uses console.log via uses EM_ASM, not
38 * printf etc., to minimize any risk of debugging or logging depending on
39 * malloc.
40 */
41
42#include <stdalign.h>
43#include <stdbool.h>
44#include <stddef.h>
45#include <stdint.h>
46#include <unistd.h>
47#include <memory.h>
48#include <assert.h>
49#include <malloc.h>
50#include <limits.h>
51#include <stdlib.h>
52
53#ifdef __EMSCRIPTEN_TRACING__
54#include <emscripten/trace.h>
55#endif
56
57// Defind by the linker to have the address of the start of the heap.
58extern unsigned char __heap_base;
59extern unsigned char __heap_end;
60
61// Behavior of right shifting a signed integer is compiler implementation defined.
62static_assert((((int32_t)0x80000000U) >> 31) == -1, "This malloc implementation requires that right-shifting a signed integer produces a sign-extending (arithmetic) shift!");
63
64// Configuration: specifies the minimum alignment that malloc()ed memory outputs. Allocation requests with smaller alignment
65// than this will yield an allocation with this much alignment.
66#define MALLOC_ALIGNMENT alignof(max_align_t)
67static_assert(alignof(max_align_t) == 16, "max_align_t must be correct");
68
69#define EMMALLOC_EXPORT __attribute__((weak))
70
71#define MIN(x, y) ((x) < (y) ? (x) : (y))
72#define MAX(x, y) ((x) > (y) ? (x) : (y))
73
74#define NUM_FREE_BUCKETS 64
75#define BUCKET_BITMASK_T uint64_t
76
77// Dynamic memory is subdivided into regions, in the format
78
79// <size:uint32_t> ..... <size:uint32_t> | <size:uint32_t> ..... <size:uint32_t> | <size:uint32_t> ..... <size:uint32_t> | .....
80
81// That is, at the bottom and top end of each memory region, the size of that region is stored. That allows traversing the
82// memory regions backwards and forwards. Because each allocation must be at least a multiple of 4 bytes, the lowest two bits of
83// each size field is unused. Free regions are distinguished by used regions by having the FREE_REGION_FLAG bit present
84// in the size field. I.e. for free regions, the size field is odd, and for used regions, the size field reads even.
85#define FREE_REGION_FLAG 0x1u
86
87// Attempts to malloc() more than this many bytes would cause an overflow when calculating the size of a region,
88// therefore allocations larger than this are short-circuited immediately on entry.
89#define MAX_ALLOC_SIZE 0xFFFFFFC7u
90
91// A free region has the following structure:
92// <size:size_t> <prevptr> <nextptr> ... <size:size_t>
93
94typedef struct Region
95{
96 size_t size;
97 // Use a circular doubly linked list to represent free region data.
98 struct Region *prev, *next;
99 // ... N bytes of free data
100 size_t _at_the_end_of_this_struct_size; // do not dereference, this is present for convenient struct sizeof() computation only
101} Region;
102
103// Each memory block starts with a RootRegion at the beginning.
104// The RootRegion specifies the size of the region block, and forms a linked
105// list of all RootRegions in the program, starting with `listOfAllRegions`
106// below.
107typedef struct RootRegion
108{
109 uint32_t size;
110 struct RootRegion *next;
111 uint8_t* endPtr;
112} RootRegion;
113
114#if defined(__EMSCRIPTEN_PTHREADS__)
115// In multithreaded builds, use a simple global spinlock strategy to acquire/release access to the memory allocator.
116static volatile uint8_t multithreadingLock = 0;
117#define MALLOC_ACQUIRE() while(__sync_lock_test_and_set(&multithreadingLock, 1)) { while(multithreadingLock) { /*nop*/ } }
118#define MALLOC_RELEASE() __sync_lock_release(&multithreadingLock)
119// Test code to ensure we have tight malloc acquire/release guards in place.
120#define ASSERT_MALLOC_IS_ACQUIRED() assert(multithreadingLock == 1)
121#else
122// In singlethreaded builds, no need for locking.
123#define MALLOC_ACQUIRE() ((void)0)
124#define MALLOC_RELEASE() ((void)0)
125#define ASSERT_MALLOC_IS_ACQUIRED() ((void)0)
126#endif
127
128#define IS_POWER_OF_2(val) (((val) & ((val)-1)) == 0)
129#define ALIGN_UP(ptr, alignment) ((uint8_t*)((((uintptr_t)(ptr)) + ((alignment)-1)) & ~((alignment)-1)))
130#define HAS_ALIGNMENT(ptr, alignment) ((((uintptr_t)(ptr)) & ((alignment)-1)) == 0)
131
132static_assert(IS_POWER_OF_2(MALLOC_ALIGNMENT), "MALLOC_ALIGNMENT must be a power of two value!");
133static_assert(MALLOC_ALIGNMENT >= 4, "Smallest possible MALLOC_ALIGNMENT if 4!");
134
135// A region that contains as payload a single forward linked list of pointers to
136// root regions of each disjoint region blocks.
137static RootRegion *listOfAllRegions = NULL;
138
139// For each of the buckets, maintain a linked list head node. The head node for each
140// free region is a sentinel node that does not actually represent any free space, but
141// the sentinel is used to avoid awkward testing against (if node == freeRegionHeadNode)
142// when adding and removing elements from the linked list, i.e. we are guaranteed that
143// the sentinel node is always fixed and there, and the actual free region list elements
144// start at freeRegionBuckets[i].next each.
145static Region freeRegionBuckets[NUM_FREE_BUCKETS] = {
146 { .prev = &freeRegionBuckets[0], .next = &freeRegionBuckets[0] },
147 { .prev = &freeRegionBuckets[1], .next = &freeRegionBuckets[1] },
148 { .prev = &freeRegionBuckets[2], .next = &freeRegionBuckets[2] },
149 { .prev = &freeRegionBuckets[3], .next = &freeRegionBuckets[3] },
150 { .prev = &freeRegionBuckets[4], .next = &freeRegionBuckets[4] },
151 { .prev = &freeRegionBuckets[5], .next = &freeRegionBuckets[5] },
152 { .prev = &freeRegionBuckets[6], .next = &freeRegionBuckets[6] },
153 { .prev = &freeRegionBuckets[7], .next = &freeRegionBuckets[7] },
154 { .prev = &freeRegionBuckets[8], .next = &freeRegionBuckets[8] },
155 { .prev = &freeRegionBuckets[9], .next = &freeRegionBuckets[9] },
156 { .prev = &freeRegionBuckets[10], .next = &freeRegionBuckets[10] },
157 { .prev = &freeRegionBuckets[11], .next = &freeRegionBuckets[11] },
158 { .prev = &freeRegionBuckets[12], .next = &freeRegionBuckets[12] },
159 { .prev = &freeRegionBuckets[13], .next = &freeRegionBuckets[13] },
160 { .prev = &freeRegionBuckets[14], .next = &freeRegionBuckets[14] },
161 { .prev = &freeRegionBuckets[15], .next = &freeRegionBuckets[15] },
162 { .prev = &freeRegionBuckets[16], .next = &freeRegionBuckets[16] },
163 { .prev = &freeRegionBuckets[17], .next = &freeRegionBuckets[17] },
164 { .prev = &freeRegionBuckets[18], .next = &freeRegionBuckets[18] },
165 { .prev = &freeRegionBuckets[19], .next = &freeRegionBuckets[19] },
166 { .prev = &freeRegionBuckets[20], .next = &freeRegionBuckets[20] },
167 { .prev = &freeRegionBuckets[21], .next = &freeRegionBuckets[21] },
168 { .prev = &freeRegionBuckets[22], .next = &freeRegionBuckets[22] },
169 { .prev = &freeRegionBuckets[23], .next = &freeRegionBuckets[23] },
170 { .prev = &freeRegionBuckets[24], .next = &freeRegionBuckets[24] },
171 { .prev = &freeRegionBuckets[25], .next = &freeRegionBuckets[25] },
172 { .prev = &freeRegionBuckets[26], .next = &freeRegionBuckets[26] },
173 { .prev = &freeRegionBuckets[27], .next = &freeRegionBuckets[27] },
174 { .prev = &freeRegionBuckets[28], .next = &freeRegionBuckets[28] },
175 { .prev = &freeRegionBuckets[29], .next = &freeRegionBuckets[29] },
176 { .prev = &freeRegionBuckets[30], .next = &freeRegionBuckets[30] },
177 { .prev = &freeRegionBuckets[31], .next = &freeRegionBuckets[31] },
178 { .prev = &freeRegionBuckets[32], .next = &freeRegionBuckets[32] },
179 { .prev = &freeRegionBuckets[33], .next = &freeRegionBuckets[33] },
180 { .prev = &freeRegionBuckets[34], .next = &freeRegionBuckets[34] },
181 { .prev = &freeRegionBuckets[35], .next = &freeRegionBuckets[35] },
182 { .prev = &freeRegionBuckets[36], .next = &freeRegionBuckets[36] },
183 { .prev = &freeRegionBuckets[37], .next = &freeRegionBuckets[37] },
184 { .prev = &freeRegionBuckets[38], .next = &freeRegionBuckets[38] },
185 { .prev = &freeRegionBuckets[39], .next = &freeRegionBuckets[39] },
186 { .prev = &freeRegionBuckets[40], .next = &freeRegionBuckets[40] },
187 { .prev = &freeRegionBuckets[41], .next = &freeRegionBuckets[41] },
188 { .prev = &freeRegionBuckets[42], .next = &freeRegionBuckets[42] },
189 { .prev = &freeRegionBuckets[43], .next = &freeRegionBuckets[43] },
190 { .prev = &freeRegionBuckets[44], .next = &freeRegionBuckets[44] },
191 { .prev = &freeRegionBuckets[45], .next = &freeRegionBuckets[45] },
192 { .prev = &freeRegionBuckets[46], .next = &freeRegionBuckets[46] },
193 { .prev = &freeRegionBuckets[47], .next = &freeRegionBuckets[47] },
194 { .prev = &freeRegionBuckets[48], .next = &freeRegionBuckets[48] },
195 { .prev = &freeRegionBuckets[49], .next = &freeRegionBuckets[49] },
196 { .prev = &freeRegionBuckets[50], .next = &freeRegionBuckets[50] },
197 { .prev = &freeRegionBuckets[51], .next = &freeRegionBuckets[51] },
198 { .prev = &freeRegionBuckets[52], .next = &freeRegionBuckets[52] },
199 { .prev = &freeRegionBuckets[53], .next = &freeRegionBuckets[53] },
200 { .prev = &freeRegionBuckets[54], .next = &freeRegionBuckets[54] },
201 { .prev = &freeRegionBuckets[55], .next = &freeRegionBuckets[55] },
202 { .prev = &freeRegionBuckets[56], .next = &freeRegionBuckets[56] },
203 { .prev = &freeRegionBuckets[57], .next = &freeRegionBuckets[57] },
204 { .prev = &freeRegionBuckets[58], .next = &freeRegionBuckets[58] },
205 { .prev = &freeRegionBuckets[59], .next = &freeRegionBuckets[59] },
206 { .prev = &freeRegionBuckets[60], .next = &freeRegionBuckets[60] },
207 { .prev = &freeRegionBuckets[61], .next = &freeRegionBuckets[61] },
208 { .prev = &freeRegionBuckets[62], .next = &freeRegionBuckets[62] },
209 { .prev = &freeRegionBuckets[63], .next = &freeRegionBuckets[63] },
210};
211
212// A bitmask that tracks the population status for each of the 64 distinct memory regions:
213// a zero at bit position i means that the free list bucket i is empty. This bitmask is
214// used to avoid redundant scanning of the 64 different free region buckets: instead by
215// looking at the bitmask we can find in constant time an index to a free region bucket
216// that contains free memory of desired size.
217static BUCKET_BITMASK_T freeRegionBucketsUsed = 0;
218
219// Amount of bytes taken up by allocation header data
220#define REGION_HEADER_SIZE (2*sizeof(size_t))
221
222// Smallest allocation size that is possible is 2*pointer size, since payload of each region must at least contain space
223// to store the free region linked list prev and next pointers. An allocation size smaller than this will be rounded up
224// to this size.
225#define SMALLEST_ALLOCATION_SIZE (2*sizeof(void*))
226
227/* Subdivide regions of free space into distinct circular doubly linked lists, where each linked list
228represents a range of free space blocks. The following function compute_free_list_bucket() converts
229an allocation size to the bucket index that should be looked at. The buckets are grouped as follows:
230
231 Bucket 0: [8, 15], range size=8
232 Bucket 1: [16, 23], range size=8
233 Bucket 2: [24, 31], range size=8
234 Bucket 3: [32, 39], range size=8
235 Bucket 4: [40, 47], range size=8
236 Bucket 5: [48, 55], range size=8
237 Bucket 6: [56, 63], range size=8
238 Bucket 7: [64, 71], range size=8
239 Bucket 8: [72, 79], range size=8
240 Bucket 9: [80, 87], range size=8
241 Bucket 10: [88, 95], range size=8
242 Bucket 11: [96, 103], range size=8
243 Bucket 12: [104, 111], range size=8
244 Bucket 13: [112, 119], range size=8
245 Bucket 14: [120, 159], range size=40
246 Bucket 15: [160, 191], range size=32
247 Bucket 16: [192, 223], range size=32
248 Bucket 17: [224, 255], range size=32
249 Bucket 18: [256, 319], range size=64
250 Bucket 19: [320, 383], range size=64
251 Bucket 20: [384, 447], range size=64
252 Bucket 21: [448, 511], range size=64
253 Bucket 22: [512, 639], range size=128
254 Bucket 23: [640, 767], range size=128
255 Bucket 24: [768, 895], range size=128
256 Bucket 25: [896, 1023], range size=128
257 Bucket 26: [1024, 1279], range size=256
258 Bucket 27: [1280, 1535], range size=256
259 Bucket 28: [1536, 1791], range size=256
260 Bucket 29: [1792, 2047], range size=256
261 Bucket 30: [2048, 2559], range size=512
262 Bucket 31: [2560, 3071], range size=512
263 Bucket 32: [3072, 3583], range size=512
264 Bucket 33: [3584, 6143], range size=2560
265 Bucket 34: [6144, 8191], range size=2048
266 Bucket 35: [8192, 12287], range size=4096
267 Bucket 36: [12288, 16383], range size=4096
268 Bucket 37: [16384, 24575], range size=8192
269 Bucket 38: [24576, 32767], range size=8192
270 Bucket 39: [32768, 49151], range size=16384
271 Bucket 40: [49152, 65535], range size=16384
272 Bucket 41: [65536, 98303], range size=32768
273 Bucket 42: [98304, 131071], range size=32768
274 Bucket 43: [131072, 196607], range size=65536
275 Bucket 44: [196608, 262143], range size=65536
276 Bucket 45: [262144, 393215], range size=131072
277 Bucket 46: [393216, 524287], range size=131072
278 Bucket 47: [524288, 786431], range size=262144
279 Bucket 48: [786432, 1048575], range size=262144
280 Bucket 49: [1048576, 1572863], range size=524288
281 Bucket 50: [1572864, 2097151], range size=524288
282 Bucket 51: [2097152, 3145727], range size=1048576
283 Bucket 52: [3145728, 4194303], range size=1048576
284 Bucket 53: [4194304, 6291455], range size=2097152
285 Bucket 54: [6291456, 8388607], range size=2097152
286 Bucket 55: [8388608, 12582911], range size=4194304
287 Bucket 56: [12582912, 16777215], range size=4194304
288 Bucket 57: [16777216, 25165823], range size=8388608
289 Bucket 58: [25165824, 33554431], range size=8388608
290 Bucket 59: [33554432, 50331647], range size=16777216
291 Bucket 60: [50331648, 67108863], range size=16777216
292 Bucket 61: [67108864, 100663295], range size=33554432
293 Bucket 62: [100663296, 134217727], range size=33554432
294 Bucket 63: 134217728 bytes and larger. */
295static_assert(NUM_FREE_BUCKETS == 64, "Following function is tailored specifically for NUM_FREE_BUCKETS == 64 case");
296static int compute_free_list_bucket(size_t allocSize)
297{
298 if (allocSize < 128) return (allocSize >> 3) - 1;
299 int clz = __builtin_clz(allocSize);
300 int bucketIndex = (clz > 19) ? 110 - (clz<<2) + ((allocSize >> (29-clz)) ^ 4) : MIN(71 - (clz<<1) + ((allocSize >> (30-clz)) ^ 2), NUM_FREE_BUCKETS-1);
301 assert(bucketIndex >= 0);
302 assert(bucketIndex < NUM_FREE_BUCKETS);
303 return bucketIndex;
304}
305
306#define DECODE_CEILING_SIZE(size) ((size_t)((size) & ~FREE_REGION_FLAG))
307
308static Region *prev_region(Region *region)
309{
310 size_t prevRegionSize = ((size_t*)region)[-1];
311 prevRegionSize = DECODE_CEILING_SIZE(prevRegionSize);
312 return (Region*)((uint8_t*)region - prevRegionSize);
313}
314
315static Region *next_region(Region *region)
316{
317 return (Region*)((uint8_t*)region + region->size);
318}
319
320static size_t region_ceiling_size(Region *region)
321{
322 return ((size_t*)((uint8_t*)region + region->size))[-1];
323}
324
325static bool region_is_free(Region *r)
326{
327 return region_ceiling_size(r) & FREE_REGION_FLAG;
328}
329
330static bool region_is_in_use(Region *r)
331{
332 return r->size == region_ceiling_size(r);
333}
334
335static size_t size_of_region_from_ceiling(Region *r)
336{
337 size_t size = region_ceiling_size(r);
338 return DECODE_CEILING_SIZE(size);
339}
340
341static bool debug_region_is_consistent(Region *r)
342{
343 assert(r);
344 size_t sizeAtBottom = r->size;
345 size_t sizeAtCeiling = size_of_region_from_ceiling(r);
346 return sizeAtBottom == sizeAtCeiling;
347}
348
349static uint8_t *region_payload_start_ptr(Region *region)
350{
351 return (uint8_t*)region + sizeof(size_t);
352}
353
354static uint8_t *region_payload_end_ptr(Region *region)
355{
356 return (uint8_t*)region + region->size - sizeof(size_t);
357}
358
359static void create_used_region(void *ptr, size_t size)
360{
361 assert(ptr);
362 assert(HAS_ALIGNMENT(ptr, sizeof(size_t)));
363 assert(HAS_ALIGNMENT(size, sizeof(size_t)));
364 assert(size >= sizeof(Region));
365 *(size_t*)ptr = size;
366 ((size_t*)ptr)[(size/sizeof(size_t))-1] = size;
367}
368
369static void create_free_region(void *ptr, size_t size)
370{
371 assert(ptr);
372 assert(HAS_ALIGNMENT(ptr, sizeof(size_t)));
373 assert(HAS_ALIGNMENT(size, sizeof(size_t)));
374 assert(size >= sizeof(Region));
375 Region *freeRegion = (Region*)ptr;
376 freeRegion->size = size;
377 ((size_t*)ptr)[(size/sizeof(size_t))-1] = size | FREE_REGION_FLAG;
378}
379
380static void prepend_to_free_list(Region *region, Region *prependTo)
381{
382 assert(region);
383 assert(prependTo);
384 // N.b. the region we are prepending to is always the sentinel node,
385 // which represents a dummy node that is technically not a free node, so
386 // region_is_free(prependTo) does not hold.
387 assert(region_is_free((Region*)region));
388 region->next = prependTo;
389 region->prev = prependTo->prev;
390 assert(region->prev);
391 prependTo->prev = region;
392 region->prev->next = region;
393}
394
395static void unlink_from_free_list(Region *region)
396{
397 assert(region);
398 assert(region_is_free((Region*)region));
399 assert(region->prev);
400 assert(region->next);
401 region->prev->next = region->next;
402 region->next->prev = region->prev;
403}
404
405static void link_to_free_list(Region *freeRegion)
406{
407 assert(freeRegion);
408 assert(freeRegion->size >= sizeof(Region));
409 int bucketIndex = compute_free_list_bucket(freeRegion->size-REGION_HEADER_SIZE);
410 Region *freeListHead = freeRegionBuckets + bucketIndex;
411 freeRegion->prev = freeListHead;
412 freeRegion->next = freeListHead->next;
413 assert(freeRegion->next);
414 freeListHead->next = freeRegion;
415 freeRegion->next->prev = freeRegion;
416 freeRegionBucketsUsed |= ((BUCKET_BITMASK_T)1) << bucketIndex;
417}
418
419#if 0
420static void dump_memory_regions()
421{
422 ASSERT_MALLOC_IS_ACQUIRED();
423 RootRegion *root = listOfAllRegions;
424 MAIN_THREAD_ASYNC_EM_ASM(console.log('All memory regions:'));
425 while(root)
426 {
427 Region *r = (Region*)root;
428 assert(debug_region_is_consistent(r));
429 uint8_t *lastRegionEnd = root->endPtr;
430 MAIN_THREAD_ASYNC_EM_ASM(console.log('Region block 0x'+($0>>>0).toString(16)+' - 0x'+($1>>>0).toString(16)+ ' ('+($2>>>0)+' bytes):'),
431 r, lastRegionEnd, lastRegionEnd-(uint8_t*)r);
432 while((uint8_t*)r < lastRegionEnd)
433 {
434 MAIN_THREAD_ASYNC_EM_ASM(console.log('Region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+')'),
435 r, r->size, region_ceiling_size(r) == r->size);
436
437 assert(debug_region_is_consistent(r));
438 size_t sizeFromCeiling = size_of_region_from_ceiling(r);
439 if (sizeFromCeiling != r->size)
440 MAIN_THREAD_ASYNC_EM_ASM(console.log('Corrupt region! Size marker at the end of the region does not match: '+($0>>>0)), sizeFromCeiling);
441 if (r->size == 0)
442 break;
443 r = next_region(r);
444 }
445 root = root->next;
446 MAIN_THREAD_ASYNC_EM_ASM(console.log(""));
447 }
448 MAIN_THREAD_ASYNC_EM_ASM(console.log('Free regions:'));
449 for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
450 {
451 Region *prev = &freeRegionBuckets[i];
452 Region *fr = freeRegionBuckets[i].next;
453 while(fr != &freeRegionBuckets[i])
454 {
455 MAIN_THREAD_ASYNC_EM_ASM(console.log('In bucket '+$0+', free region 0x'+($1>>>0).toString(16)+', size: ' + ($2>>>0) + ' (size at ceiling: '+($3>>>0)+'), prev: 0x' + ($4>>>0).toString(16) + ', next: 0x' + ($5>>>0).toString(16)),
456 i, fr, fr->size, size_of_region_from_ceiling(fr), fr->prev, fr->next);
457 assert(debug_region_is_consistent(fr));
458 assert(region_is_free(fr));
459 assert(fr->prev == prev);
460 prev = fr;
461 assert(fr->next != fr);
462 assert(fr->prev != fr);
463 fr = fr->next;
464 }
465 }
466 MAIN_THREAD_ASYNC_EM_ASM(console.log('Free bucket index map: ' + ($0>>>0).toString(2) + ' ' + ($1>>>0).toString(2)), (uint32_t)(freeRegionBucketsUsed >> 32), (uint32_t)freeRegionBucketsUsed);
467 MAIN_THREAD_ASYNC_EM_ASM(console.log(""));
468}
469
470void emmalloc_dump_memory_regions()
471{
472 MALLOC_ACQUIRE();
473 dump_memory_regions();
474 MALLOC_RELEASE();
475}
476
477static int validate_memory_regions()
478{
479 ASSERT_MALLOC_IS_ACQUIRED();
480 RootRegion *root = listOfAllRegions;
481 while(root)
482 {
483 Region *r = (Region*)root;
484 if (!debug_region_is_consistent(r))
485 {
486 MAIN_THREAD_ASYNC_EM_ASM(console.error('Used region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+') is corrupt (size markers in the beginning and at the end of the region do not match!)'),
487 r, r->size, region_ceiling_size(r) == r->size);
488 return 1;
489 }
490 uint8_t *lastRegionEnd = root->endPtr;
491 while((uint8_t*)r < lastRegionEnd)
492 {
493 if (!debug_region_is_consistent(r))
494 {
495 MAIN_THREAD_ASYNC_EM_ASM(console.error('Used region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+') is corrupt (size markers in the beginning and at the end of the region do not match!)'),
496 r, r->size, region_ceiling_size(r) == r->size);
497 return 1;
498 }
499 if (r->size == 0)
500 break;
501 r = next_region(r);
502 }
503 root = root->next;
504 }
505 for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
506 {
507 Region *prev = &freeRegionBuckets[i];
508 Region *fr = freeRegionBuckets[i].next;
509 while(fr != &freeRegionBuckets[i])
510 {
511 if (!debug_region_is_consistent(fr) || !region_is_free(fr) || fr->prev != prev || fr->next == fr || fr->prev == fr)
512 {
513 MAIN_THREAD_ASYNC_EM_ASM(console.log('In bucket '+$0+', free region 0x'+($1>>>0).toString(16)+', size: ' + ($2>>>0) + ' (size at ceiling: '+($3>>>0)+'), prev: 0x' + ($4>>>0).toString(16) + ', next: 0x' + ($5>>>0).toString(16) + ' is corrupt!'),
514 i, fr, fr->size, size_of_region_from_ceiling(fr), fr->prev, fr->next);
515 return 1;
516 }
517 prev = fr;
518 fr = fr->next;
519 }
520 }
521 return 0;
522}
523
524int emmalloc_validate_memory_regions()
525{
526 MALLOC_ACQUIRE();
527 int memoryError = validate_memory_regions();
528 MALLOC_RELEASE();
529 return memoryError;
530}
531#endif
532
533static bool claim_more_memory(size_t numBytes)
534{
535#ifdef EMMALLOC_VERBOSE
536 MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory(numBytes='+($0>>>0)+ ')'), numBytes);
537#endif
538
539#ifdef EMMALLOC_MEMVALIDATE
540 validate_memory_regions();
541#endif
542
543 uint8_t *startPtr;
544 uint8_t *endPtr;
545 do {
546 // If this is the first time we're called, see if we can use
547 // the initial heap memory set up by wasm-ld.
548 if (!listOfAllRegions) {
549 unsigned char *heap_base = &__heap_base;
550 unsigned char *heap_end = &__heap_end;
551 if (heap_end < heap_base) {
552 __builtin_trap();
553 }
554 if (numBytes <= (size_t)(heap_end - heap_base)) {
555 startPtr = heap_base;
556 endPtr = heap_end;
557 break;
558 }
559 }
560
561 // Round numBytes up to the nearest page size.
562 numBytes = (numBytes + (PAGE_SIZE-1)) & -PAGE_SIZE;
563
564 // Claim memory via sbrk
565 startPtr = (uint8_t*)sbrk(numBytes);
566 if ((intptr_t)startPtr == -1)
567 {
568#ifdef EMMALLOC_VERBOSE
569 MAIN_THREAD_ASYNC_EM_ASM(console.error('claim_more_memory: sbrk failed!'));
570#endif
571 return false;
572 }
573#ifdef EMMALLOC_VERBOSE
574 MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed 0x' + ($0>>>0).toString(16) + ' - 0x' + ($1>>>0).toString(16) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
575#endif
576 assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
577 endPtr = startPtr + numBytes;
578 } while (0);
579
580 // Create a sentinel region at the end of the new heap block
581 Region *endSentinelRegion = (Region*)(endPtr - sizeof(Region));
582 create_used_region(endSentinelRegion, sizeof(Region));
583
584 // If we are the sole user of sbrk(), it will feed us continuous/consecutive memory addresses - take advantage
585 // of that if so: instead of creating two disjoint memory regions blocks, expand the previous one to a larger size.
586 uint8_t *previousSbrkEndAddress = listOfAllRegions ? listOfAllRegions->endPtr : 0;
587 if (startPtr == previousSbrkEndAddress)
588 {
589 Region *prevEndSentinel = prev_region((Region*)startPtr);
590 assert(debug_region_is_consistent(prevEndSentinel));
591 assert(region_is_in_use(prevEndSentinel));
592 Region *prevRegion = prev_region(prevEndSentinel);
593 assert(debug_region_is_consistent(prevRegion));
594
595 listOfAllRegions->endPtr = endPtr;
596
597 // Two scenarios, either the last region of the previous block was in use, in which case we need to create
598 // a new free region in the newly allocated space; or it was free, in which case we can extend that region
599 // to cover a larger size.
600 if (region_is_free(prevRegion))
601 {
602 size_t newFreeRegionSize = (uint8_t*)endSentinelRegion - (uint8_t*)prevRegion;
603 unlink_from_free_list(prevRegion);
604 create_free_region(prevRegion, newFreeRegionSize);
605 link_to_free_list(prevRegion);
606 return true;
607 }
608 // else: last region of the previous block was in use. Since we are joining two consecutive sbrk() blocks,
609 // we can swallow the end sentinel of the previous block away.
610 startPtr -= sizeof(Region);
611 }
612 else
613 {
614 // Create a root region at the start of the heap block
615 create_used_region(startPtr, sizeof(Region));
616
617 // Dynamic heap start region:
618 RootRegion *newRegionBlock = (RootRegion*)startPtr;
619 newRegionBlock->next = listOfAllRegions; // Pointer to next region block head
620 newRegionBlock->endPtr = endPtr; // Pointer to the end address of this region block
621 listOfAllRegions = newRegionBlock;
622 startPtr += sizeof(Region);
623 }
624
625 // Create a new memory region for the new claimed free space.
626 create_free_region(startPtr, (uint8_t*)endSentinelRegion - startPtr);
627 link_to_free_list((Region*)startPtr);
628 return true;
629}
630
631#if 0
632// Initialize emmalloc during static initialization.
633// See system/lib/README.md for static constructor ordering.
634__attribute__((constructor(47)))
635static void initialize_emmalloc_heap()
636{
637 // Initialize circular doubly linked lists representing free space
638 // Never useful to unroll this for loop, just takes up code size.
639#pragma clang loop unroll(disable)
640 for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
641 freeRegionBuckets[i].prev = freeRegionBuckets[i].next = &freeRegionBuckets[i];
642
643#ifdef EMMALLOC_VERBOSE
644 MAIN_THREAD_ASYNC_EM_ASM(console.log('initialize_emmalloc_heap()'));
645#endif
646
647 // Start with a tiny dynamic region.
648 claim_more_memory(3*sizeof(Region));
649}
650
651void emmalloc_blank_slate_from_orbit()
652{
653 MALLOC_ACQUIRE();
654 listOfAllRegions = NULL;
655 freeRegionBucketsUsed = 0;
656 initialize_emmalloc_heap();
657 MALLOC_RELEASE();
658}
659#endif
660
661static void *attempt_allocate(Region *freeRegion, size_t alignment, size_t size)
662{
663 ASSERT_MALLOC_IS_ACQUIRED();
664 assert(freeRegion);
665 // Look at the next potential free region to allocate into.
666 // First, we should check if the free region has enough of payload bytes contained
667 // in it to accommodate the new allocation. This check needs to take account the
668 // requested allocation alignment, so the payload memory area needs to be rounded
669 // upwards to the desired alignment.
670 uint8_t *payloadStartPtr = region_payload_start_ptr(freeRegion);
671 uint8_t *payloadStartPtrAligned = ALIGN_UP(payloadStartPtr, alignment);
672 uint8_t *payloadEndPtr = region_payload_end_ptr(freeRegion);
673
674 // Do we have enough free space, taking into account alignment?
675 if (payloadStartPtrAligned + size > payloadEndPtr)
676 return NULL;
677
678 // We have enough free space, so the memory allocation will be made into this region. Remove this free region
679 // from the list of free regions: whatever slop remains will be later added back to the free region pool.
680 unlink_from_free_list(freeRegion);
681
682 // Before we proceed further, fix up the boundary of this region and the region that precedes this one,
683 // so that the boundary between the two regions happens at a right spot for the payload to be aligned.
684 if (payloadStartPtr != payloadStartPtrAligned)
685 {
686 Region *prevRegion = prev_region((Region*)freeRegion);
687 // We never have two free regions adjacent to each other, so the region before this free
688 // region should be in use.
689 assert(region_is_in_use(prevRegion));
690 size_t regionBoundaryBumpAmount = payloadStartPtrAligned - payloadStartPtr;
691 size_t newThisRegionSize = freeRegion->size - regionBoundaryBumpAmount;
692 create_used_region(prevRegion, prevRegion->size + regionBoundaryBumpAmount);
693 freeRegion = (Region *)((uint8_t*)freeRegion + regionBoundaryBumpAmount);
694 freeRegion->size = newThisRegionSize;
695 }
696 // Next, we need to decide whether this region is so large that it should be split into two regions,
697 // one representing the newly used memory area, and at the high end a remaining leftover free area.
698 // This splitting to two is done always if there is enough space for the high end to fit a region.
699 // Carve 'size' bytes of payload off this region. So,
700 // [sz prev next sz]
701 // becomes
702 // [sz payload sz] [sz prev next sz]
703 if (sizeof(Region) + REGION_HEADER_SIZE + size <= freeRegion->size)
704 {
705 // There is enough space to keep a free region at the end of the carved out block
706 // -> construct the new block
707 Region *newFreeRegion = (Region *)((uint8_t*)freeRegion + REGION_HEADER_SIZE + size);
708 create_free_region(newFreeRegion, freeRegion->size - size - REGION_HEADER_SIZE);
709 link_to_free_list(newFreeRegion);
710
711 // Recreate the resized Region under its new size.
712 create_used_region(freeRegion, size + REGION_HEADER_SIZE);
713 }
714 else
715 {
716 // There is not enough space to split the free memory region into used+free parts, so consume the whole
717 // region as used memory, not leaving a free memory region behind.
718 // Initialize the free region as used by resetting the ceiling size to the same value as the size at bottom.
719 ((size_t*)((uint8_t*)freeRegion + freeRegion->size))[-1] = freeRegion->size;
720 }
721
722#ifdef __EMSCRIPTEN_TRACING__
723 emscripten_trace_record_allocation(freeRegion, freeRegion->size);
724#endif
725
726#ifdef EMMALLOC_VERBOSE
727 MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_allocate - succeeded allocating memory, region ptr=0x' + ($0>>>0).toString(16) + ', align=' + $1 + ', payload size=' + ($2>>>0) + ' bytes)'), freeRegion, alignment, size);
728#endif
729
730 return (uint8_t*)freeRegion + sizeof(size_t);
731}
732
733static size_t validate_alloc_alignment(size_t alignment)
734{
735 // Cannot perform allocations that are less than 4 byte aligned, because the Region
736 // control structures need to be aligned. Also round up to minimum outputted alignment.
737 alignment = MAX(alignment, MALLOC_ALIGNMENT);
738 // Arbitrary upper limit on alignment - very likely a programming bug if alignment is higher than this.
739 assert(alignment <= 1024*1024);
740 return alignment;
741}
742
743static size_t validate_alloc_size(size_t size)
744{
745 assert(size + REGION_HEADER_SIZE > size);
746
747 // Allocation sizes must be a multiple of pointer sizes, and at least 2*sizeof(pointer).
748 size_t validatedSize = size > SMALLEST_ALLOCATION_SIZE ? (size_t)ALIGN_UP(size, sizeof(Region*)) : SMALLEST_ALLOCATION_SIZE;
749 assert(validatedSize >= size); // 32-bit wraparound should not occur, too large sizes should be stopped before
750
751 return validatedSize;
752}
753
754static void *allocate_memory(size_t alignment, size_t size)
755{
756 ASSERT_MALLOC_IS_ACQUIRED();
757
758#ifdef EMMALLOC_VERBOSE
759 MAIN_THREAD_ASYNC_EM_ASM(console.log('allocate_memory(align=' + $0 + ', size=' + ($1>>>0) + ' bytes)'), alignment, size);
760#endif
761
762#ifdef EMMALLOC_MEMVALIDATE
763 validate_memory_regions();
764#endif
765
766 if (!IS_POWER_OF_2(alignment))
767 {
768#ifdef EMMALLOC_VERBOSE
769 MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: alignment not power of 2!'));
770#endif
771 return 0;
772 }
773
774 if (size > MAX_ALLOC_SIZE)
775 {
776#ifdef EMMALLOC_VERBOSE
777 MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
778#endif
779 return 0;
780 }
781
782 alignment = validate_alloc_alignment(alignment);
783 size = validate_alloc_size(size);
784
785 // Attempt to allocate memory starting from smallest bucket that can contain the required amount of memory.
786 // Under normal alignment conditions this should always be the first or second bucket we look at, but if
787 // performing an allocation with complex alignment, we may need to look at multiple buckets.
788 int bucketIndex = compute_free_list_bucket(size);
789 BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed >> bucketIndex;
790
791 // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
792 while(bucketMask)
793 {
794 BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
795 bucketIndex += indexAdd;
796 bucketMask >>= indexAdd;
797 assert(bucketIndex >= 0);
798 assert(bucketIndex <= NUM_FREE_BUCKETS-1);
799 assert(freeRegionBucketsUsed & (((BUCKET_BITMASK_T)1) << bucketIndex));
800
801 Region *freeRegion = freeRegionBuckets[bucketIndex].next;
802 assert(freeRegion);
803 if (freeRegion != &freeRegionBuckets[bucketIndex])
804 {
805 void *ptr = attempt_allocate(freeRegion, alignment, size);
806 if (ptr)
807 return ptr;
808
809 // We were not able to allocate from the first region found in this bucket, so penalize
810 // the region by cycling it to the end of the doubly circular linked list. (constant time)
811 // This provides a randomized guarantee that when performing allocations of size k to a
812 // bucket of [k-something, k+something] range, we will not always attempt to satisfy the
813 // allocation from the same available region at the front of the list, but we try each
814 // region in turn.
815 unlink_from_free_list(freeRegion);
816 prepend_to_free_list(freeRegion, &freeRegionBuckets[bucketIndex]);
817 // But do not stick around to attempt to look at other regions in this bucket - move
818 // to search the next populated bucket index if this did not fit. This gives a practical
819 // "allocation in constant time" guarantee, since the next higher bucket will only have
820 // regions that are all of strictly larger size than the requested allocation. Only if
821 // there is a difficult alignment requirement we may fail to perform the allocation from
822 // a region in the next bucket, and if so, we keep trying higher buckets until one of them
823 // works.
824 ++bucketIndex;
825 bucketMask >>= 1;
826 }
827 else
828 {
829 // This bucket was not populated after all with any regions,
830 // but we just had a stale bit set to mark a populated bucket.
831 // Reset the bit to update latest status so that we do not
832 // redundantly look at this bucket again.
833 freeRegionBucketsUsed &= ~(((BUCKET_BITMASK_T)1) << bucketIndex);
834 bucketMask ^= 1;
835 }
836 // Instead of recomputing bucketMask from scratch at the end of each loop, it is updated as we go,
837 // to avoid undefined behavior with (x >> 32)/(x >> 64) when bucketIndex reaches 32/64, (the shift would comes out as a no-op instead of 0).
838
839 assert((bucketIndex == NUM_FREE_BUCKETS && bucketMask == 0) || (bucketMask == freeRegionBucketsUsed >> bucketIndex));
840 }
841
842 // None of the buckets were able to accommodate an allocation. If this happens we are almost out of memory.
843 // The largest bucket might contain some suitable regions, but we only looked at one region in that bucket, so
844 // as a last resort, loop through more free regions in the bucket that represents the largest allocations available.
845 // But only if the bucket representing largest allocations available is not any of the first thirty buckets,
846 // these represent allocatable areas less than <1024 bytes - which could be a lot of scrap.
847 // In such case, prefer to sbrk() in more memory right away.
848 int largestBucketIndex = NUM_FREE_BUCKETS - 1 - __builtin_clzll(freeRegionBucketsUsed);
849 // freeRegion will be null if there is absolutely no memory left. (all buckets are 100% used)
850 Region *freeRegion = freeRegionBucketsUsed ? freeRegionBuckets[largestBucketIndex].next : 0;
851 if (freeRegionBucketsUsed >> 30)
852 {
853 // Look only at a constant number of regions in this bucket max, to avoid bad worst case behavior.
854 // If this many regions cannot find free space, we give up and prefer to sbrk() more instead.
855 const int maxRegionsToTryBeforeGivingUp = 99;
856 int numTriesLeft = maxRegionsToTryBeforeGivingUp;
857 while(freeRegion != &freeRegionBuckets[largestBucketIndex] && numTriesLeft-- > 0)
858 {
859 void *ptr = attempt_allocate(freeRegion, alignment, size);
860 if (ptr)
861 return ptr;
862 freeRegion = freeRegion->next;
863 }
864 }
865
866 // We were unable to find a free memory region. Must sbrk() in more memory!
867 size_t numBytesToClaim = size+sizeof(Region)*3;
868 assert(numBytesToClaim > size); // 32-bit wraparound should not happen here, allocation size has been validated above!
869 bool success = claim_more_memory(numBytesToClaim);
870 if (success)
871 return allocate_memory(alignment, size); // Recurse back to itself to try again
872
873 // also sbrk() failed, we are really really constrained :( As a last resort, go back to looking at the
874 // bucket we already looked at above, continuing where the above search left off - perhaps there are
875 // regions we overlooked the first time that might be able to satisfy the allocation.
876 if (freeRegion)
877 {
878 while(freeRegion != &freeRegionBuckets[largestBucketIndex])
879 {
880 void *ptr = attempt_allocate(freeRegion, alignment, size);
881 if (ptr)
882 return ptr;
883 freeRegion = freeRegion->next;
884 }
885 }
886
887#ifdef EMMALLOC_VERBOSE
888 MAIN_THREAD_ASYNC_EM_ASM(console.log('Could not find a free memory block!'));
889#endif
890
891 return 0;
892}
893
894static
895void *emmalloc_memalign(size_t alignment, size_t size)
896{
897 MALLOC_ACQUIRE();
898 void *ptr = allocate_memory(alignment, size);
899 MALLOC_RELEASE();
900 return ptr;
901}
902
903#if 0
904void * EMMALLOC_EXPORT memalign(size_t alignment, size_t size)
905{
906 return emmalloc_memalign(alignment, size);
907}
908#endif
909
910void * EMMALLOC_EXPORT aligned_alloc(size_t alignment, size_t size)
911{
912 if ((alignment % sizeof(void *) != 0) || (size % alignment) != 0)
913 return 0;
914 return emmalloc_memalign(alignment, size);
915}
916
917static
918void *emmalloc_malloc(size_t size)
919{
920 return emmalloc_memalign(MALLOC_ALIGNMENT, size);
921}
922
923void * EMMALLOC_EXPORT malloc(size_t size)
924{
925 return emmalloc_malloc(size);
926}
927
928static
929size_t emmalloc_usable_size(void *ptr)
930{
931 if (!ptr)
932 return 0;
933
934 uint8_t *regionStartPtr = (uint8_t*)ptr - sizeof(size_t);
935 Region *region = (Region*)(regionStartPtr);
936 assert(HAS_ALIGNMENT(region, sizeof(size_t)));
937
938 MALLOC_ACQUIRE();
939
940 size_t size = region->size;
941 assert(size >= sizeof(Region));
942 assert(region_is_in_use(region));
943
944 MALLOC_RELEASE();
945
946 return size - REGION_HEADER_SIZE;
947}
948
949size_t EMMALLOC_EXPORT malloc_usable_size(void *ptr)
950{
951 return emmalloc_usable_size(ptr);
952}
953
954static
955void emmalloc_free(void *ptr)
956{
957#ifdef EMMALLOC_MEMVALIDATE
958 emmalloc_validate_memory_regions();
959#endif
960
961 if (!ptr)
962 return;
963
964#ifdef EMMALLOC_VERBOSE
965 MAIN_THREAD_ASYNC_EM_ASM(console.log('free(ptr=0x'+($0>>>0).toString(16)+')'), ptr);
966#endif
967
968 uint8_t *regionStartPtr = (uint8_t*)ptr - sizeof(size_t);
969 Region *region = (Region*)(regionStartPtr);
970 assert(HAS_ALIGNMENT(region, sizeof(size_t)));
971
972 MALLOC_ACQUIRE();
973
974 size_t size = region->size;
975#ifdef EMMALLOC_VERBOSE
976 if (size < sizeof(Region) || !region_is_in_use(region))
977 {
978 if (debug_region_is_consistent(region))
979 // LLVM wasm backend bug: cannot use MAIN_THREAD_ASYNC_EM_ASM() here, that generates internal compiler error
980 // Reproducible by running e.g. other.test_alloc_3GB
981 EM_ASM(console.error('Double free at region ptr 0x' + ($0>>>0).toString(16) + ', region->size: 0x' + ($1>>>0).toString(16) + ', region->sizeAtCeiling: 0x' + ($2>>>0).toString(16) + ')'), region, size, region_ceiling_size(region));
982 else
983 MAIN_THREAD_ASYNC_EM_ASM(console.error('Corrupt region at region ptr 0x' + ($0>>>0).toString(16) + ' region->size: 0x' + ($1>>>0).toString(16) + ', region->sizeAtCeiling: 0x' + ($2>>>0).toString(16) + ')'), region, size, region_ceiling_size(region));
984 }
985#endif
986 assert(size >= sizeof(Region));
987 assert(region_is_in_use(region));
988
989#ifdef __EMSCRIPTEN_TRACING__
990 emscripten_trace_record_free(region);
991#endif
992
993 // Check merging with left side
994 size_t prevRegionSizeField = ((size_t*)region)[-1];
995 size_t prevRegionSize = prevRegionSizeField & ~FREE_REGION_FLAG;
996 if (prevRegionSizeField != prevRegionSize) // Previous region is free?
997 {
998 Region *prevRegion = (Region*)((uint8_t*)region - prevRegionSize);
999 assert(debug_region_is_consistent(prevRegion));
1000 unlink_from_free_list(prevRegion);
1001 regionStartPtr = (uint8_t*)prevRegion;
1002 size += prevRegionSize;
1003 }
1004
1005 // Check merging with right side
1006 Region *nextRegion = next_region(region);
1007 assert(debug_region_is_consistent(nextRegion));
1008 size_t sizeAtEnd = *(size_t*)region_payload_end_ptr(nextRegion);
1009 if (nextRegion->size != sizeAtEnd)
1010 {
1011 unlink_from_free_list(nextRegion);
1012 size += nextRegion->size;
1013 }
1014
1015 create_free_region(regionStartPtr, size);
1016 link_to_free_list((Region*)regionStartPtr);
1017
1018 MALLOC_RELEASE();
1019
1020#ifdef EMMALLOC_MEMVALIDATE
1021 emmalloc_validate_memory_regions();
1022#endif
1023}
1024
1025void EMMALLOC_EXPORT free(void *ptr)
1026{
1027 emmalloc_free(ptr);
1028}
1029
1030// Can be called to attempt to increase or decrease the size of the given region
1031// to a new size (in-place). Returns 1 if resize succeeds, and 0 on failure.
1032static int attempt_region_resize(Region *region, size_t size)
1033{
1034 ASSERT_MALLOC_IS_ACQUIRED();
1035 assert(size > 0);
1036 assert(HAS_ALIGNMENT(size, sizeof(size_t)));
1037
1038#ifdef EMMALLOC_VERBOSE
1039 MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_region_resize(region=0x' + ($0>>>0).toString(16) + ', size=' + ($1>>>0) + ' bytes)'), region, size);
1040#endif
1041
1042 // First attempt to resize this region, if the next region that follows this one
1043 // is a free region.
1044 Region *nextRegion = next_region(region);
1045 uint8_t *nextRegionEndPtr = (uint8_t*)nextRegion + nextRegion->size;
1046 size_t sizeAtCeiling = ((size_t*)nextRegionEndPtr)[-1];
1047 if (nextRegion->size != sizeAtCeiling) // Next region is free?
1048 {
1049 assert(region_is_free(nextRegion));
1050 uint8_t *newNextRegionStartPtr = (uint8_t*)region + size;
1051 assert(HAS_ALIGNMENT(newNextRegionStartPtr, sizeof(size_t)));
1052 // Next region does not shrink to too small size?
1053 if (newNextRegionStartPtr + sizeof(Region) <= nextRegionEndPtr)
1054 {
1055 unlink_from_free_list(nextRegion);
1056 create_free_region(newNextRegionStartPtr, nextRegionEndPtr - newNextRegionStartPtr);
1057 link_to_free_list((Region*)newNextRegionStartPtr);
1058 create_used_region(region, newNextRegionStartPtr - (uint8_t*)region);
1059 return 1;
1060 }
1061 // If we remove the next region altogether, allocation is satisfied?
1062 if (newNextRegionStartPtr <= nextRegionEndPtr)
1063 {
1064 unlink_from_free_list(nextRegion);
1065 create_used_region(region, region->size + nextRegion->size);
1066 return 1;
1067 }
1068 }
1069 else
1070 {
1071 // Next region is an used region - we cannot change its starting address. However if we are shrinking the
1072 // size of this region, we can create a new free region between this and the next used region.
1073 if (size + sizeof(Region) <= region->size)
1074 {
1075 size_t freeRegionSize = region->size - size;
1076 create_used_region(region, size);
1077 Region *freeRegion = (Region *)((uint8_t*)region + size);
1078 create_free_region(freeRegion, freeRegionSize);
1079 link_to_free_list(freeRegion);
1080 return 1;
1081 }
1082 else if (size <= region->size)
1083 {
1084 // Caller was asking to shrink the size, but due to not being able to fit a full Region in the shrunk
1085 // area, we cannot actually do anything. This occurs if the shrink amount is really small. In such case,
1086 // just call it success without doing any work.
1087 return 1;
1088 }
1089 }
1090#ifdef EMMALLOC_VERBOSE
1091 MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_region_resize failed.'));
1092#endif
1093 return 0;
1094}
1095
1096static int acquire_and_attempt_region_resize(Region *region, size_t size)
1097{
1098 MALLOC_ACQUIRE();
1099 int success = attempt_region_resize(region, size);
1100 MALLOC_RELEASE();
1101 return success;
1102}
1103
1104static
1105void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size)
1106{
1107#ifdef EMMALLOC_VERBOSE
1108 MAIN_THREAD_ASYNC_EM_ASM(console.log('aligned_realloc(ptr=0x' + ($0>>>0).toString(16) + ', alignment=' + $1 + ', size=' + ($2>>>0)), ptr, alignment, size);
1109#endif
1110
1111 if (!ptr)
1112 return emmalloc_memalign(alignment, size);
1113
1114 if (size == 0)
1115 {
1116 free(ptr);
1117 return 0;
1118 }
1119
1120 if (size > MAX_ALLOC_SIZE)
1121 {
1122#ifdef EMMALLOC_VERBOSE
1123 MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1124#endif
1125 return 0;
1126 }
1127
1128 assert(IS_POWER_OF_2(alignment));
1129 // aligned_realloc() cannot be used to ask to change the alignment of a pointer.
1130 assert(HAS_ALIGNMENT(ptr, alignment));
1131 size = validate_alloc_size(size);
1132
1133 // Calculate the region start address of the original allocation
1134 Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1135
1136 // First attempt to resize the given region to avoid having to copy memory around
1137 if (acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE))
1138 {
1139#ifdef __EMSCRIPTEN_TRACING__
1140 emscripten_trace_record_reallocation(ptr, ptr, size);
1141#endif
1142 return ptr;
1143 }
1144
1145 // If resize failed, we must allocate a new region, copy the data over, and then
1146 // free the old region.
1147 void *newptr = emmalloc_memalign(alignment, size);
1148 if (newptr)
1149 {
1150 memcpy(newptr, ptr, MIN(size, region->size - REGION_HEADER_SIZE));
1151 free(ptr);
1152 }
1153 // N.B. If there is not enough memory, the old memory block should not be freed and
1154 // null pointer is returned.
1155 return newptr;
1156}
1157
1158#if 0
1159void * EMMALLOC_EXPORT aligned_realloc(void *ptr, size_t alignment, size_t size)
1160{
1161 return emmalloc_aligned_realloc(ptr, alignment, size);
1162}
1163#endif
1164
1165#if 0
1166// realloc_try() is like realloc(), but only attempts to try to resize the existing memory
1167// area. If resizing the existing memory area fails, then realloc_try() will return 0
1168// (the original memory block is not freed or modified). If resizing succeeds, previous
1169// memory contents will be valid up to min(old length, new length) bytes.
1170void *emmalloc_realloc_try(void *ptr, size_t size)
1171{
1172 if (!ptr)
1173 return 0;
1174
1175 if (size == 0)
1176 {
1177 free(ptr);
1178 return 0;
1179 }
1180
1181 if (size > MAX_ALLOC_SIZE)
1182 {
1183#ifdef EMMALLOC_VERBOSE
1184 MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1185#endif
1186 return 0;
1187 }
1188
1189 size = validate_alloc_size(size);
1190
1191 // Calculate the region start address of the original allocation
1192 Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1193
1194 // Attempt to resize the given region to avoid having to copy memory around
1195 int success = acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE);
1196#ifdef __EMSCRIPTEN_TRACING__
1197 if (success)
1198 emscripten_trace_record_reallocation(ptr, ptr, size);
1199#endif
1200 return success ? ptr : 0;
1201}
1202
1203// emmalloc_aligned_realloc_uninitialized() is like aligned_realloc(), but old memory contents
1204// will be undefined after reallocation. (old memory is not preserved in any case)
1205void *emmalloc_aligned_realloc_uninitialized(void *ptr, size_t alignment, size_t size)
1206{
1207 if (!ptr)
1208 return emmalloc_memalign(alignment, size);
1209
1210 if (size == 0)
1211 {
1212 free(ptr);
1213 return 0;
1214 }
1215
1216 if (size > MAX_ALLOC_SIZE)
1217 {
1218#ifdef EMMALLOC_VERBOSE
1219 MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1220#endif
1221 return 0;
1222 }
1223
1224 size = validate_alloc_size(size);
1225
1226 // Calculate the region start address of the original allocation
1227 Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1228
1229 // First attempt to resize the given region to avoid having to copy memory around
1230 if (acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE))
1231 {
1232#ifdef __EMSCRIPTEN_TRACING__
1233 emscripten_trace_record_reallocation(ptr, ptr, size);
1234#endif
1235 return ptr;
1236 }
1237
1238 // If resize failed, drop the old region and allocate a new region. Memory is not
1239 // copied over
1240 free(ptr);
1241 return emmalloc_memalign(alignment, size);
1242}
1243#endif
1244
1245static
1246void *emmalloc_realloc(void *ptr, size_t size)
1247{
1248 return emmalloc_aligned_realloc(ptr, MALLOC_ALIGNMENT, size);
1249}
1250
1251void * EMMALLOC_EXPORT realloc(void *ptr, size_t size)
1252{
1253 return emmalloc_realloc(ptr, size);
1254}
1255
1256#if 0
1257// realloc_uninitialized() is like realloc(), but old memory contents
1258// will be undefined after reallocation. (old memory is not preserved in any case)
1259void *emmalloc_realloc_uninitialized(void *ptr, size_t size)
1260{
1261 return emmalloc_aligned_realloc_uninitialized(ptr, MALLOC_ALIGNMENT, size);
1262}
1263#endif
1264
1265static
1266int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size)
1267{
1268 assert(memptr);
1269 if (alignment % sizeof(void *) != 0)
1270 return 22/* EINVAL*/;
1271 *memptr = emmalloc_memalign(alignment, size);
1272 return *memptr ? 0 : 12/*ENOMEM*/;
1273}
1274
1275int EMMALLOC_EXPORT posix_memalign(void **memptr, size_t alignment, size_t size)
1276{
1277 return emmalloc_posix_memalign(memptr, alignment, size);
1278}
1279
1280static
1281void *emmalloc_calloc(size_t num, size_t size)
1282{
1283 size_t bytes = num*size;
1284 void *ptr = emmalloc_memalign(MALLOC_ALIGNMENT, bytes);
1285 if (ptr)
1286 memset(ptr, 0, bytes);
1287 return ptr;
1288}
1289
1290void * EMMALLOC_EXPORT calloc(size_t num, size_t size)
1291{
1292 return emmalloc_calloc(num, size);
1293}
1294
1295#if 0
1296static int count_linked_list_size(Region *list)
1297{
1298 int size = 1;
1299 for(Region *i = list->next; i != list; list = list->next)
1300 ++size;
1301 return size;
1302}
1303
1304static size_t count_linked_list_space(Region *list)
1305{
1306 size_t space = 0;
1307 for(Region *i = list->next; i != list; list = list->next)
1308 space += region_payload_end_ptr(i) - region_payload_start_ptr(i);
1309 return space;
1310}
1311
1312struct mallinfo emmalloc_mallinfo()
1313{
1314 MALLOC_ACQUIRE();
1315
1316 struct mallinfo info;
1317 // Non-mmapped space allocated (bytes): For emmalloc,
1318 // let's define this as the difference between heap size and dynamic top end.
1319 info.arena = emscripten_get_heap_size() - (size_t)sbrk(0);
1320 // Number of "ordinary" blocks. Let's define this as the number of highest
1321 // size blocks. (subtract one from each, since there is a sentinel node in each list)
1322 info.ordblks = count_linked_list_size(&freeRegionBuckets[NUM_FREE_BUCKETS-1])-1;
1323 // Number of free "fastbin" blocks. For emmalloc, define this as the number
1324 // of blocks that are not in the largest pristine block.
1325 info.smblks = 0;
1326 // The total number of bytes in free "fastbin" blocks.
1327 info.fsmblks = 0;
1328 for(int i = 0; i < NUM_FREE_BUCKETS-1; ++i)
1329 {
1330 info.smblks += count_linked_list_size(&freeRegionBuckets[i])-1;
1331 info.fsmblks += count_linked_list_space(&freeRegionBuckets[i]);
1332 }
1333
1334 info.hblks = 0; // Number of mmapped regions: always 0. (no mmap support)
1335 info.hblkhd = 0; // Amount of bytes in mmapped regions: always 0. (no mmap support)
1336
1337 // Walk through all the heap blocks to report the following data:
1338 // The "highwater mark" for allocated space—that is, the maximum amount of
1339 // space that was ever allocated. Emmalloc does not want to pay code to
1340 // track this, so this is only reported from current allocation data, and
1341 // may not be accurate.
1342 info.usmblks = 0;
1343 info.uordblks = 0; // The total number of bytes used by in-use allocations.
1344 info.fordblks = 0; // The total number of bytes in free blocks.
1345 // The total amount of releasable free space at the top of the heap.
1346 // This is the maximum number of bytes that could ideally be released by malloc_trim(3).
1347 Region *lastActualRegion = prev_region((Region*)(listOfAllRegions->endPtr - sizeof(Region)));
1348 info.keepcost = region_is_free(lastActualRegion) ? lastActualRegion->size : 0;
1349
1350 RootRegion *root = listOfAllRegions;
1351 while(root)
1352 {
1353 Region *r = (Region*)root;
1354 assert(debug_region_is_consistent(r));
1355 uint8_t *lastRegionEnd = root->endPtr;
1356 while((uint8_t*)r < lastRegionEnd)
1357 {
1358 assert(debug_region_is_consistent(r));
1359
1360 if (region_is_free(r))
1361 {
1362 // Count only the payload of the free block towards free memory.
1363 info.fordblks += region_payload_end_ptr(r) - region_payload_start_ptr(r);
1364 // But the header data of the free block goes towards used memory.
1365 info.uordblks += REGION_HEADER_SIZE;
1366 }
1367 else
1368 {
1369 info.uordblks += r->size;
1370 }
1371 // Update approximate watermark data
1372 info.usmblks = MAX(info.usmblks, (intptr_t)r + r->size);
1373
1374 if (r->size == 0)
1375 break;
1376 r = next_region(r);
1377 }
1378 root = root->next;
1379 }
1380
1381 MALLOC_RELEASE();
1382 return info;
1383}
1384
1385struct mallinfo EMMALLOC_EXPORT mallinfo()
1386{
1387 return emmalloc_mallinfo();
1388}
1389
1390// Note! This function is not fully multithreadin safe: while this function is running, other threads should not be
1391// allowed to call sbrk()!
1392static int trim_dynamic_heap_reservation(size_t pad)
1393{
1394 ASSERT_MALLOC_IS_ACQUIRED();
1395
1396 if (!listOfAllRegions)
1397 return 0; // emmalloc is not controlling any dynamic memory at all - cannot release memory.
1398 uint8_t *previousSbrkEndAddress = listOfAllRegions->endPtr;
1399 assert(sbrk(0) == previousSbrkEndAddress);
1400 size_t lastMemoryRegionSize = ((size_t*)previousSbrkEndAddress)[-1];
1401 assert(lastMemoryRegionSize == 16); // // The last memory region should be a sentinel node of exactly 16 bytes in size.
1402 Region *endSentinelRegion = (Region*)(previousSbrkEndAddress - sizeof(Region));
1403 Region *lastActualRegion = prev_region(endSentinelRegion);
1404
1405 // Round padding up to multiple of 4 bytes to keep sbrk() and memory region alignment intact.
1406 // Also have at least 8 bytes of payload so that we can form a full free region.
1407 size_t newRegionSize = (size_t)ALIGN_UP(pad, 4);
1408 if (pad > 0)
1409 newRegionSize += sizeof(Region) - (newRegionSize - pad);
1410
1411 if (!region_is_free(lastActualRegion) || lastActualRegion->size <= newRegionSize)
1412 return 0; // Last actual region is in use, or caller desired to leave more free memory intact than there is.
1413
1414 // This many bytes will be shrunk away.
1415 size_t shrinkAmount = lastActualRegion->size - newRegionSize;
1416 assert(HAS_ALIGNMENT(shrinkAmount, 4));
1417
1418 unlink_from_free_list(lastActualRegion);
1419 // If pad == 0, we should delete the last free region altogether. If pad > 0,
1420 // shrink the last free region to the desired size.
1421 if (newRegionSize > 0)
1422 {
1423 create_free_region(lastActualRegion, newRegionSize);
1424 link_to_free_list(lastActualRegion);
1425 }
1426
1427 // Recreate the sentinel region at the end of the last free region
1428 endSentinelRegion = (Region*)((uint8_t*)lastActualRegion + newRegionSize);
1429 create_used_region(endSentinelRegion, sizeof(Region));
1430
1431 // And update the size field of the whole region block.
1432 listOfAllRegions->endPtr = (uint8_t*)endSentinelRegion + sizeof(Region);
1433
1434 // Finally call sbrk() to shrink the memory area.
1435 void *oldSbrk = sbrk(-(intptr_t)shrinkAmount);
1436 assert((intptr_t)oldSbrk != -1); // Shrinking with sbrk() should never fail.
1437 assert(oldSbrk == previousSbrkEndAddress); // Another thread should not have raced to increase sbrk() on us!
1438
1439 // All successful, and we actually trimmed memory!
1440 return 1;
1441}
1442
1443int emmalloc_trim(size_t pad)
1444{
1445 MALLOC_ACQUIRE();
1446 int success = trim_dynamic_heap_reservation(pad);
1447 MALLOC_RELEASE();
1448 return success;
1449}
1450
1451int EMMALLOC_EXPORT malloc_trim(size_t pad)
1452{
1453 return emmalloc_trim(pad);
1454}
1455
1456size_t emmalloc_dynamic_heap_size()
1457{
1458 size_t dynamicHeapSize = 0;
1459
1460 MALLOC_ACQUIRE();
1461 RootRegion *root = listOfAllRegions;
1462 while(root)
1463 {
1464 dynamicHeapSize += root->endPtr - (uint8_t*)root;
1465 root = root->next;
1466 }
1467 MALLOC_RELEASE();
1468 return dynamicHeapSize;
1469}
1470
1471size_t emmalloc_free_dynamic_memory()
1472{
1473 size_t freeDynamicMemory = 0;
1474
1475 int bucketIndex = 0;
1476
1477 MALLOC_ACQUIRE();
1478 BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed;
1479
1480 // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
1481 while(bucketMask)
1482 {
1483 BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
1484 bucketIndex += indexAdd;
1485 bucketMask >>= indexAdd;
1486 for(Region *freeRegion = freeRegionBuckets[bucketIndex].next;
1487 freeRegion != &freeRegionBuckets[bucketIndex];
1488 freeRegion = freeRegion->next)
1489 {
1490 freeDynamicMemory += freeRegion->size - REGION_HEADER_SIZE;
1491 }
1492 ++bucketIndex;
1493 bucketMask >>= 1;
1494 }
1495 MALLOC_RELEASE();
1496 return freeDynamicMemory;
1497}
1498
1499size_t emmalloc_compute_free_dynamic_memory_fragmentation_map(size_t freeMemorySizeMap[32])
1500{
1501 memset((void*)freeMemorySizeMap, 0, sizeof(freeMemorySizeMap[0])*32);
1502
1503 size_t numFreeMemoryRegions = 0;
1504 int bucketIndex = 0;
1505 MALLOC_ACQUIRE();
1506 BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed;
1507
1508 // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
1509 while(bucketMask)
1510 {
1511 BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
1512 bucketIndex += indexAdd;
1513 bucketMask >>= indexAdd;
1514 for(Region *freeRegion = freeRegionBuckets[bucketIndex].next;
1515 freeRegion != &freeRegionBuckets[bucketIndex];
1516 freeRegion = freeRegion->next)
1517 {
1518 ++numFreeMemoryRegions;
1519 size_t freeDynamicMemory = freeRegion->size - REGION_HEADER_SIZE;
1520 if (freeDynamicMemory > 0)
1521 ++freeMemorySizeMap[31-__builtin_clz(freeDynamicMemory)];
1522 else
1523 ++freeMemorySizeMap[0];
1524 }
1525 ++bucketIndex;
1526 bucketMask >>= 1;
1527 }
1528 MALLOC_RELEASE();
1529 return numFreeMemoryRegions;
1530}
1531
1532size_t emmalloc_unclaimed_heap_memory(void) {
1533 return emscripten_get_heap_max() - (size_t)sbrk(0);
1534}
1535#endif
1536
1537// Define these to satisfy musl references.
1538void *__libc_malloc(size_t) __attribute__((alias("malloc")));
1539void __libc_free(void *) __attribute__((alias("free")));
1540void *__libc_calloc(size_t nmemb, size_t size) __attribute__((alias("calloc")));