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