diff --git a/tests/allocators/buddy/assembly/buddy.ts b/tests/allocators/buddy/assembly/buddy.ts new file mode 100644 index 00000000..1816529c --- /dev/null +++ b/tests/allocators/buddy/assembly/buddy.ts @@ -0,0 +1,527 @@ +/* + Copyright 2018 Evan Wallace + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +*/// see: https://github.com/evanw/buddy-malloc + +/* + * This file implements a buddy memory allocator, which is an allocator that + * allocates memory within a fixed linear address range. It spans the address + * range with a binary tree that tracks free space. Both "malloc" and "free" + * are O(log N) time where N is the maximum possible number of allocations. + * + * The "buddy" term comes from how the tree is used. When memory is allocated, + * nodes in the tree are split recursively until a node of the appropriate size + * is reached. Every split results in two child nodes, each of which is the + * buddy of the other. When a node is freed, the node and its buddy can be + * merged again if the buddy is also free. This makes the memory available + * for larger allocations again. + */ + +/* + * Every allocation needs an 8-byte header to store the allocation size while + * staying 8-byte aligned. The address returned by "malloc" is the address + * right after this header (i.e. the size occupies the 8 bytes before the + * returned address). + */ +const HEADER_SIZE: usize = 8; + +/* + * The minimum allocation size is 16 bytes because we have an 8-byte header and + * we need to stay 8-byte aligned. + */ +const MIN_ALLOC_LOG2: usize = 4; +const MIN_ALLOC: usize = 1 << MIN_ALLOC_LOG2; + +/* + * The maximum allocation size is currently set to 2gb. This is the total size + * of the heap. It's technically also the maximum allocation size because the + * heap could consist of a single allocation of this size. But of course real + * heaps will have multiple allocations, so the real maximum allocation limit + * is at most 1gb. + */ +const MAX_ALLOC_LOG2: usize = 31; +const MAX_ALLOC: usize = 1 << MAX_ALLOC_LOG2; + +/* + * Allocations are done in powers of two starting from MIN_ALLOC and ending at + * MAX_ALLOC inclusive. Each allocation size has a bucket that stores the free + * list for that allocation size. + * + * Given a bucket index, the size of the allocations in that bucket can be + * found with "(size_t)1 << (MAX_ALLOC_LOG2 - bucket)". + */ +const BUCKET_COUNT: usize = MAX_ALLOC_LOG2 - MIN_ALLOC_LOG2 + 1; + +/* + * Free lists are stored as circular doubly-linked lists. Every possible + * allocation size has an associated free list that is threaded through all + * currently free blocks of that size. That means MIN_ALLOC must be at least + * "sizeof(list_t)". MIN_ALLOC is currently 16 bytes, so this will be true for + * both 32-bit and 64-bit. + */ +@unmanaged +class List { + prev: List; + next: List; + static readonly SIZE: usize = 2 * sizeof(); +} + +/* + * Each bucket corresponds to a certain allocation size and stores a free list + * for that size. The bucket at index 0 corresponds to an allocation size of + * MAX_ALLOC (i.e. the whole address space). + */ +var BUCKET_START: usize = HEAP_BASE; +var BUCKET_END: usize = BUCKET_START + BUCKET_COUNT * sizeof(); + +function get_bucket(index: usize): List { + assert(index < BUCKET_COUNT); + return load(BUCKET_START + index * sizeof()); +} + +function set_bucket(index: usize, list: List): void { + assert(index < BUCKET_COUNT); + store(BUCKET_START + index * sizeof(), list); +} + +/* + * We could initialize the allocator by giving it one free block the size of + * the entire address space. However, this would cause us to instantly reserve + * half of the entire address space on the first allocation, since the first + * split would store a free list entry at the start of the right child of the + * root. Instead, we have the tree start out small and grow the size of the + * tree as we use more memory. The size of the tree is tracked by this value. + */ +var bucket_limit: usize; + +/* + * This array represents a linearized binary tree of bits. Every possible + * allocation larger than MIN_ALLOC has a node in this tree (and therefore a + * bit in this array). + * + * Given the index for a node, lineraized binary trees allow you to traverse to + * the parent node or the child nodes just by doing simple arithmetic on the + * index: + * + * - Move to parent: index = (index - 1) / 2; + * - Move to left child: index = index * 2 + 1; + * - Move to right child: index = index * 2 + 2; + * - Move to sibling: index = ((index - 1) ^ 1) + 1; + * + * Each node in this tree can be in one of several states: + * + * - UNUSED (both children are UNUSED) + * - SPLIT (one child is UNUSED and the other child isn't) + * - USED (neither children are UNUSED) + * + * These states take two bits to store. However, it turns out we have enough + * information to distinguish between UNUSED and USED from context, so we only + * need to store SPLIT or not, which only takes a single bit. + * + * Note that we don't need to store any nodes for allocations of size MIN_ALLOC + * since we only ever care about parent nodes. + */ +const SPLIT_COUNT: usize = (1 << (BUCKET_COUNT - 1)) / 8; +var SPLIT_START: usize = BUCKET_END; +var SPLIT_END: usize = SPLIT_START + SPLIT_COUNT * sizeof(); + +function node_is_split(index: usize): i32 { + assert(index < SPLIT_COUNT); + return load(SPLIT_START + index); +} + +function node_set_split(index: usize, state: i32): void { + assert(index < SPLIT_COUNT); + store(SPLIT_START + index, state); +} + +/* + * This is the starting address of the address range for this allocator. Every + * returned allocation will be an offset of this pointer from 0 to MAX_ALLOC. + */ +var base_ptr: usize; + +/* + * This is the maximum address that has ever been used by the allocator. It's + * used to know when to call "brk" to request more memory from the kernel. + */ +var max_ptr: usize; + +/* + * Make sure all addresses before "new_value" are valid and can be used. Memory + * is allocated in a 2gb address range but that memory is not reserved up + * front. It's only reserved when it's needed by calling this function. This + * will return false if the memory could not be reserved. + */ +function update_max_ptr(new_value: usize): bool { + if (new_value > max_ptr) { + var pages = (((new_value - max_ptr) + 0xffff) & ~0xffff) >>> 16; + if (grow_memory(pages) < 0) { + return false; + } + max_ptr = new_value; + } + return true; +} + +/* + * Initialize a list to empty. Because these are circular lists, an "empty" + * list is an entry where both links point to itself. This makes insertion + * and removal simpler because they don't need any branches. + */ +function list_init(list: List): void { + list.prev = list; + list.next = list; +} + +/* + * Append the provided entry to the end of the list. This assumes the entry + * isn't in a list already because it overwrites the linked list pointers. + */ +function list_push(list: List, entry: List): void { + var prev = list.prev; + entry.prev = prev; + entry.next = list; + prev.next = entry; + list.prev = entry; +} + +/* + * Remove the provided entry from whichever list it's currently in. This + * assumes that the entry is in a list. You don't need to provide the list + * because the lists are circular, so the list's pointers will automatically + * be updated if the first or last entries are removed. + */ +function list_remove(entry: List): void { + var prev = entry.prev; + var next = entry.next; + prev.next = next; + next.prev = prev; +} + +/* + * Remove and return the first entry in the list or NULL if the list is empty. + */ +function list_pop(list: List): List | null { + var back = list.prev; + if (back == list) return null; + list_remove(back); + return back; +} + +/* + * This maps from the index of a node to the address of memory that node + * represents. The bucket can be derived from the index using a loop but is + * required to be provided here since having them means we can avoid the loop + * and have this function return in constant time. + */ +function ptr_for_node(index: usize, bucket: usize): usize { + return base_ptr + ((index - (1 << bucket) + 1) << (MAX_ALLOC_LOG2 - bucket)); +} + +/* + * This maps from an address of memory to the node that represents that + * address. There are often many nodes that all map to the same address, so + * the bucket is needed to uniquely identify a node. + */ +function node_for_ptr(ptr: usize, bucket: usize): usize { + return ((ptr - base_ptr) >> (MAX_ALLOC_LOG2 - bucket)) + (1 << bucket) - 1; +} + +/* + * Given the index of a node, this returns the "is split" flag of the parent. + */ +function parent_is_split(index: usize): i32 { + index = (index - 1) / 2; + return (node_is_split(index / 8) >> (index % 8)) & 1; +} + +/* + * Given the index of a node, this flips the "is split" flag of the parent. + */ +function flip_parent_is_split(index: usize): void { + index = (index - 1) / 2; + var indexDiv8 = index / 8; + node_set_split(indexDiv8, node_is_split(indexDiv8) ^ (1 << (index % 8))); +} + +/* + * Given the requested size passed to "malloc", this function returns the index + * of the smallest bucket that can fit that size. + */ +function bucket_for_request(request: usize): usize { + var bucket = BUCKET_COUNT - 1; + var size = MIN_ALLOC; + + while (size < request) { + bucket--; + size *= 2; + } + + return bucket; +} + +/* + * The tree is always rooted at the current bucket limit. This call grows the + * tree by repeatedly doubling it in size until the root lies at the provided + * bucket index. Each doubling lowers the bucket limit by 1. + */ +function lower_bucket_limit(bucket: usize): u32 { + while (bucket < bucket_limit) { + var root = node_for_ptr(base_ptr, bucket_limit); + var right_child: usize; + + /* + * If the parent isn't SPLIT, that means the node at the current bucket + * limit is UNUSED and our address space is entirely free. In that case, + * clear the root free list, increase the bucket limit, and add a single + * block with the newly-expanded address space to the new root free list. + */ + if (!parent_is_split(root)) { + list_remove(changetype(base_ptr)); + list_init(get_bucket(--bucket_limit)); + list_push(get_bucket(bucket_limit), changetype(base_ptr)); + continue; + } + + /* + * Otherwise, the tree is currently in use. Create a parent node for the + * current root node in the SPLIT state with a right child on the free + * list. Make sure to reserve the memory for the free list entry before + * writing to it. Note that we do not need to flip the "is split" flag for + * our current parent because it's already on (we know because we just + * checked it above). + */ + right_child = ptr_for_node(root + 1, bucket_limit); + if (!update_max_ptr(right_child + List.SIZE)) { + return 0; + } + list_push(get_bucket(bucket_limit), changetype(right_child)); + list_init(get_bucket(--bucket_limit)); + + /* + * Set the grandparent's SPLIT flag so if we need to lower the bucket limit + * again, we'll know that the new root node we just added is in use. + */ + root = (root - 1) / 2; + if (root != 0) { + flip_parent_is_split(root); + } + } + + return 1; +} + +@global +function allocate_memory(request: usize): usize { + var original_bucket: usize, bucket: usize; + + /* + * Make sure it's possible for an allocation of this size to succeed. There's + * a hard-coded limit on the maximum allocation size because of the way this + * allocator works. + */ + if (request + HEADER_SIZE > MAX_ALLOC) { + return 0; + } + + /* + * Initialize our global state if this is the first call to "malloc". At the + * beginning, the tree has a single node that represents the smallest + * possible allocation size. More memory will be reserved later as needed. + */ + if (base_ptr == 0) { + base_ptr = max_ptr = SPLIT_END; + bucket_limit = BUCKET_COUNT - 1; + update_max_ptr(base_ptr + List.SIZE); + list_init(get_bucket(BUCKET_COUNT - 1)); + list_push(get_bucket(BUCKET_COUNT - 1), changetype(base_ptr)); + } + + /* + * Find the smallest bucket that will fit this request. This doesn't check + * that there's space for the request yet. + */ + bucket = bucket_for_request(request + HEADER_SIZE); + original_bucket = bucket; + + /* + * Search for a bucket with a non-empty free list that's as large or larger + * than what we need. If there isn't an exact match, we'll need to split a + * larger one to get a match. + */ + while (bucket + 1 != 0) { + var size: usize, bytes_needed: usize, i: usize; + var ptr: usize; + + /* + * We may need to grow the tree to be able to fit an allocation of this + * size. Try to grow the tree and stop here if we can't. + */ + if (!lower_bucket_limit(bucket)) { + return 0; + } + + /* + * Try to pop a block off the free list for this bucket. If the free list + * is empty, we're going to have to split a larger block instead. + */ + ptr = changetype(list_pop(get_bucket(bucket))); + if (!ptr) { + /* + * If we're not at the root of the tree or it's impossible to grow the + * tree any more, continue on to the next bucket. + */ + if (bucket != bucket_limit || bucket == 0) { + bucket--; + continue; + } + + /* + * Otherwise, grow the tree one more level and then pop a block off the + * free list again. Since we know the root of the tree is used (because + * the free list was empty), this will add a parent above this node in + * the SPLIT state and then add the new right child node to the free list + * for this bucket. Popping the free list will give us this right child. + */ + if (!lower_bucket_limit(bucket - 1)) { + return 0; + } + ptr = changetype(list_pop(get_bucket(bucket))); + } + + /* + * Try to expand the address space first before going any further. If we + * have run out of space, put this block back on the free list and fail. + */ + size = 1 << (MAX_ALLOC_LOG2 - bucket); + bytes_needed = bucket < original_bucket ? size / 2 + List.SIZE : size; + if (!update_max_ptr(ptr + bytes_needed)) { + list_push(get_bucket(bucket), changetype(ptr)); + return 0; + } + + /* + * If we got a node off the free list, change the node from UNUSED to USED. + * This involves flipping our parent's "is split" bit because that bit is + * the exclusive-or of the UNUSED flags of both children, and our UNUSED + * flag (which isn't ever stored explicitly) has just changed. + * + * Note that we shouldn't ever need to flip the "is split" bit of our + * grandparent because we know our buddy is USED so it's impossible for our + * grandparent to be UNUSED (if our buddy chunk was UNUSED, our parent + * wouldn't ever have been split in the first place). + */ + i = node_for_ptr(ptr, bucket); + if (i != 0) { + flip_parent_is_split(i); + } + + /* + * If the node we got is larger than we need, split it down to the correct + * size and put the new unused child nodes on the free list in the + * corresponding bucket. This is done by repeatedly moving to the left + * child, splitting the parent, and then adding the right child to the free + * list. + */ + while (bucket < original_bucket) { + i = i * 2 + 1; + bucket++; + flip_parent_is_split(i); + list_push(get_bucket(bucket), changetype(ptr_for_node(i + 1, bucket))); + } + + /* + * Now that we have a memory address, write the block header (just the size + * of the allocation) and return the address immediately after the header. + */ + store(ptr, request); + return ptr + HEADER_SIZE; + } + + return 0; +} + +@global +function free_memory(ptr: usize): void { + var bucket: usize, i: usize; + + /* + * Ignore any attempts to free a NULL pointer. + */ + if (!ptr) { + return; + } + + /* + * We were given the address returned by "malloc" so get back to the actual + * address of the node by subtracting off the size of the block header. Then + * look up the index of the node corresponding to this address. + */ + ptr = ptr - HEADER_SIZE; + bucket = bucket_for_request(load(ptr) + HEADER_SIZE); + i = node_for_ptr(ptr, bucket); + + /* + * Traverse up to the root node, flipping USED blocks to UNUSED and merging + * UNUSED buddies together into a single UNUSED parent. + */ + while (i != 0) { + /* + * Change this node from UNUSED to USED. This involves flipping our + * parent's "is split" bit because that bit is the exclusive-or of the + * UNUSED flags of both children, and our UNUSED flag (which isn't ever + * stored explicitly) has just changed. + */ + flip_parent_is_split(i); + + /* + * If the parent is now SPLIT, that means our buddy is USED, so don't merge + * with it. Instead, stop the iteration here and add ourselves to the free + * list for our bucket. + * + * Also stop here if we're at the current root node, even if that root node + * is now UNUSED. Root nodes don't have a buddy so we can't merge with one. + */ + if (parent_is_split(i) || bucket == bucket_limit) { + break; + } + + /* + * If we get here, we know our buddy is UNUSED. In this case we should + * merge with that buddy and continue traversing up to the root node. We + * need to remove the buddy from its free list here but we don't need to + * add the merged parent to its free list yet. That will be done once after + * this loop is finished. + */ + list_remove(changetype(ptr_for_node(((i - 1) ^ 1) + 1, bucket))); + i = (i - 1) / 2; + bucket--; + } + + /* + * Add ourselves to the free list for our bucket. We add to the back of the + * list because "malloc" takes from the back of the list and we want a "free" + * followed by a "malloc" of the same size to ideally use the same address + * for better memory locality. + */ + list_push(get_bucket(bucket), changetype(ptr_for_node(i, bucket))); +} diff --git a/tests/allocators/buddy/assembly/index.ts b/tests/allocators/buddy/assembly/index.ts new file mode 100644 index 00000000..8f384e66 --- /dev/null +++ b/tests/allocators/buddy/assembly/index.ts @@ -0,0 +1,2 @@ +import "./buddy"; +export { allocate_memory, free_memory }; diff --git a/tests/allocators/buddy/buddy.optimized.wat b/tests/allocators/buddy/buddy.optimized.wat new file mode 100644 index 00000000..c57ac95a --- /dev/null +++ b/tests/allocators/buddy/buddy.optimized.wat @@ -0,0 +1,1083 @@ +(module + (type $ii (func (param i32) (result i32))) + (type $iv (func (param i32))) + (type $iiv (func (param i32 i32))) + (type $iii (func (param i32 i32) (result i32))) + (type $v (func)) + (global $assembly/buddy/BUCKET_START (mut i32) (i32.const 0)) + (global $assembly/buddy/BUCKET_END (mut i32) (i32.const 0)) + (global $assembly/buddy/bucket_limit (mut i32) (i32.const 0)) + (global $assembly/buddy/SPLIT_START (mut i32) (i32.const 0)) + (global $assembly/buddy/SPLIT_END (mut i32) (i32.const 0)) + (global $assembly/buddy/base_ptr (mut i32) (i32.const 0)) + (global $assembly/buddy/max_ptr (mut i32) (i32.const 0)) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "allocate_memory" (func $assembly/buddy/allocate_memory)) + (export "free_memory" (func $assembly/buddy/free_memory)) + (export "memory" (memory $0)) + (start $start) + (func $assembly/buddy/update_max_ptr (; 0 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:175:2 + (if + ;;@ assembly/buddy.ts:175:6 + (i32.gt_u + (get_local $0) + ;;@ assembly/buddy.ts:175:18 + (get_global $assembly/buddy/max_ptr) + ) + ;;@ assembly/buddy.ts:175:27 + (block + ;;@ assembly/buddy.ts:177:4 + (if + ;;@ assembly/buddy.ts:177:8 + (i32.lt_s + (grow_memory + ;;@ assembly/buddy.ts:176:16 + (i32.shr_u + (i32.and + ;;@ assembly/buddy.ts:176:17 + (i32.add + ;;@ assembly/buddy.ts:176:18 + (i32.sub + ;;@ assembly/buddy.ts:176:19 + (get_local $0) + ;;@ assembly/buddy.ts:176:31 + (get_global $assembly/buddy/max_ptr) + ) + ;;@ assembly/buddy.ts:176:42 + (i32.const 65535) + ) + (i32.const -65536) + ) + ;;@ assembly/buddy.ts:176:65 + (i32.const 16) + ) + ) + ;;@ assembly/buddy.ts:177:29 + (i32.const 0) + ) + ;;@ assembly/buddy.ts:178:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:180:4 + (set_global $assembly/buddy/max_ptr + ;;@ assembly/buddy.ts:180:14 + (get_local $0) + ) + ) + ) + ;;@ assembly/buddy.ts:182:9 + (i32.const 1) + ) + (func $assembly/buddy/get_bucket (; 1 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:97:9 + (i32.load + ;;@ assembly/buddy.ts:97:20 + (i32.add + (get_global $assembly/buddy/BUCKET_START) + ;;@ assembly/buddy.ts:97:35 + (i32.mul + (get_local $0) + ;;@ assembly/buddy.ts:97:43 + (i32.const 4) + ) + ) + ) + ) + (func $assembly/buddy/list_init (; 2 ;) (type $iv) (param $0 i32) + ;;@ assembly/buddy.ts:191:2 + (i32.store + (get_local $0) + ;;@ assembly/buddy.ts:191:14 + (get_local $0) + ) + ;;@ assembly/buddy.ts:192:2 + (i32.store offset=4 + (get_local $0) + ;;@ assembly/buddy.ts:192:14 + (get_local $0) + ) + ) + (func $assembly/buddy/list_push (; 3 ;) (type $iiv) (param $0 i32) (param $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:201:2 + (i32.store + (get_local $1) + ;;@ assembly/buddy.ts:200:2 + (tee_local $2 + ;;@ assembly/buddy.ts:200:13 + (i32.load + (get_local $0) + ) + ) + ) + ;;@ assembly/buddy.ts:202:2 + (i32.store offset=4 + (get_local $1) + ;;@ assembly/buddy.ts:202:15 + (get_local $0) + ) + ;;@ assembly/buddy.ts:203:2 + (i32.store offset=4 + (get_local $2) + ;;@ assembly/buddy.ts:203:14 + (get_local $1) + ) + ;;@ assembly/buddy.ts:204:2 + (i32.store + (get_local $0) + ;;@ assembly/buddy.ts:204:14 + (get_local $1) + ) + ) + (func $assembly/buddy/bucket_for_request (; 4 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:271:2 + (set_local $1 + ;;@ assembly/buddy.ts:271:15 + (i32.const 27) + ) + ;;@ assembly/buddy.ts:272:2 + (set_local $2 + ;;@ assembly/buddy.ts:272:13 + (i32.const 16) + ) + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:274:9 + (i32.lt_u + (get_local $2) + ;;@ assembly/buddy.ts:274:16 + (get_local $0) + ) + (block + ;;@ assembly/buddy.ts:275:4 + (set_local $1 + (i32.sub + (get_local $1) + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:276:4 + (set_local $2 + (i32.mul + (get_local $2) + ;;@ assembly/buddy.ts:276:12 + (i32.const 2) + ) + ) + (br $continue|0) + ) + ) + ) + ;;@ assembly/buddy.ts:279:9 + (get_local $1) + ) + (func $assembly/buddy/node_for_ptr (; 5 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + ;;@ assembly/buddy.ts:246:9 + (i32.sub + (i32.add + (i32.shr_u + ;;@ assembly/buddy.ts:246:10 + (i32.sub + ;;@ assembly/buddy.ts:246:11 + (get_local $0) + ;;@ assembly/buddy.ts:246:17 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:246:30 + (i32.sub + ;;@ assembly/buddy.ts:246:31 + (i32.const 31) + ;;@ assembly/buddy.ts:246:48 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:246:59 + (i32.shl + ;;@ assembly/buddy.ts:246:60 + (i32.const 1) + ;;@ assembly/buddy.ts:246:65 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:246:75 + (i32.const 1) + ) + ) + (func $assembly/buddy/node_is_split (; 6 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:148:9 + (i32.load8_u + ;;@ assembly/buddy.ts:148:18 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + ;;@ assembly/buddy.ts:148:32 + (get_local $0) + ) + ) + ) + (func $assembly/buddy/parent_is_split (; 7 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:254:9 + (i32.and + (i32.shr_s + ;;@ assembly/buddy.ts:254:10 + (call $assembly/buddy/node_is_split + ;;@ assembly/buddy.ts:254:24 + (i32.div_u + ;;@ assembly/buddy.ts:253:2 + (tee_local $0 + ;;@ assembly/buddy.ts:253:10 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:253:11 + (get_local $0) + ;;@ assembly/buddy.ts:253:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:253:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:254:32 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:254:38 + (i32.rem_u + ;;@ assembly/buddy.ts:254:44 + (get_local $0) + ;;@ assembly/buddy.ts:254:52 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:254:58 + (i32.const 1) + ) + ) + (func $assembly/buddy/list_remove (; 8 ;) (type $iv) (param $0 i32) + (local $1 i32) + ;;@ assembly/buddy.ts:216:2 + (i32.store offset=4 + ;;@ assembly/buddy.ts:214:2 + (tee_local $1 + ;;@ assembly/buddy.ts:214:13 + (i32.load + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:215:2 + (tee_local $0 + ;;@ assembly/buddy.ts:215:13 + (i32.load offset=4 + (get_local $0) + ) + ) + ) + ;;@ assembly/buddy.ts:217:2 + (i32.store + (get_local $0) + ;;@ assembly/buddy.ts:217:14 + (get_local $1) + ) + ) + (func $assembly/buddy/ptr_for_node (; 9 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + ;;@ assembly/buddy.ts:237:9 + (i32.add + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:237:20 + (i32.shl + ;;@ assembly/buddy.ts:237:21 + (i32.add + ;;@ assembly/buddy.ts:237:22 + (i32.sub + (get_local $0) + ;;@ assembly/buddy.ts:237:30 + (i32.shl + ;;@ assembly/buddy.ts:237:31 + (i32.const 1) + ;;@ assembly/buddy.ts:237:36 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:237:46 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:237:52 + (i32.sub + ;;@ assembly/buddy.ts:237:53 + (i32.const 31) + ;;@ assembly/buddy.ts:237:70 + (get_local $1) + ) + ) + ) + ) + (func $assembly/buddy/flip_parent_is_split (; 10 ;) (type $iv) (param $0 i32) + (local $1 i32) + (local $2 i32) + (set_local $2 + ;;@ assembly/buddy.ts:262:2 + (tee_local $1 + ;;@ assembly/buddy.ts:262:18 + (i32.div_u + ;;@ assembly/buddy.ts:261:2 + (tee_local $0 + ;;@ assembly/buddy.ts:261:10 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:261:11 + (get_local $0) + ;;@ assembly/buddy.ts:261:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:261:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:262:26 + (i32.const 8) + ) + ) + ) + (set_local $0 + ;;@ assembly/buddy.ts:263:28 + (i32.xor + (call $assembly/buddy/node_is_split + ;;@ assembly/buddy.ts:263:42 + (get_local $1) + ) + ;;@ assembly/buddy.ts:263:55 + (i32.shl + ;;@ assembly/buddy.ts:263:61 + (i32.const 1) + ;;@ assembly/buddy.ts:263:66 + (i32.rem_u + ;;@ assembly/buddy.ts:263:67 + (get_local $0) + ;;@ assembly/buddy.ts:263:75 + (i32.const 8) + ) + ) + ) + ) + (i32.store8 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + (get_local $2) + ) + (get_local $0) + ) + ) + (func $assembly/buddy/lower_bucket_limit (; 11 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:288:9 + (i32.lt_u + (get_local $0) + ;;@ assembly/buddy.ts:288:18 + (get_global $assembly/buddy/bucket_limit) + ) + (block + ;;@ assembly/buddy.ts:298:4 + (if + ;;@ assembly/buddy.ts:298:8 + (i32.eqz + ;;@ assembly/buddy.ts:298:9 + (call $assembly/buddy/parent_is_split + ;;@ assembly/buddy.ts:289:4 + (tee_local $1 + ;;@ assembly/buddy.ts:289:15 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:289:28 + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:289:38 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:298:32 + (block + ;;@ assembly/buddy.ts:299:6 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:299:18 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:300:6 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:300:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:300:27 + (block (result i32) + (set_global $assembly/buddy/bucket_limit + (i32.sub + ;;@ assembly/buddy.ts:300:29 + (get_global $assembly/buddy/bucket_limit) + (i32.const 1) + ) + ) + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ;;@ assembly/buddy.ts:301:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:301:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:301:27 + (get_global $assembly/buddy/bucket_limit) + ) + ;;@ assembly/buddy.ts:301:42 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:302:6 + (br $continue|0) + ) + ) + ;;@ assembly/buddy.ts:314:4 + (if + ;;@ assembly/buddy.ts:314:8 + (i32.eqz + ;;@ assembly/buddy.ts:314:9 + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:314:24 + (i32.add + ;;@ assembly/buddy.ts:313:4 + (tee_local $2 + ;;@ assembly/buddy.ts:313:18 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:313:31 + (i32.add + (get_local $1) + ;;@ assembly/buddy.ts:313:38 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:313:41 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ;;@ assembly/buddy.ts:314:38 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:315:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:317:4 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:317:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:317:25 + (get_global $assembly/buddy/bucket_limit) + ) + ;;@ assembly/buddy.ts:317:40 + (get_local $2) + ) + ;;@ assembly/buddy.ts:318:4 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:318:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:318:25 + (block (result i32) + (set_global $assembly/buddy/bucket_limit + (i32.sub + ;;@ assembly/buddy.ts:318:27 + (get_global $assembly/buddy/bucket_limit) + (i32.const 1) + ) + ) + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ;;@ assembly/buddy.ts:325:4 + (if + ;;@ assembly/buddy.ts:324:4 + (tee_local $1 + ;;@ assembly/buddy.ts:324:11 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:324:12 + (get_local $1) + ;;@ assembly/buddy.ts:324:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:324:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:326:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:326:27 + (get_local $1) + ) + ) + (br $continue|0) + ) + ) + ) + ;;@ assembly/buddy.ts:330:9 + (i32.const 1) + ) + (func $assembly/buddy/list_pop (; 12 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + ;;@ assembly/buddy.ts:225:2 + (if + ;;@ assembly/buddy.ts:225:6 + (i32.eq + ;;@ assembly/buddy.ts:224:2 + (tee_local $1 + ;;@ assembly/buddy.ts:224:13 + (i32.load + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:225:14 + (get_local $0) + ) + ;;@ assembly/buddy.ts:225:27 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:226:2 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:226:14 + (get_local $1) + ) + ;;@ assembly/buddy.ts:227:9 + (get_local $1) + ) + (func $assembly/buddy/allocate_memory (; 13 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + ;;@ assembly/buddy.ts:342:2 + (if + ;;@ assembly/buddy.ts:342:6 + (i32.gt_u + (i32.add + (get_local $0) + ;;@ assembly/buddy.ts:342:16 + (i32.const 8) + ) + ;;@ assembly/buddy.ts:342:30 + (i32.const -2147483648) + ) + ;;@ assembly/buddy.ts:343:11 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:351:2 + (if + (i32.eqz + ;;@ assembly/buddy.ts:351:6 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:351:21 + (block + ;;@ assembly/buddy.ts:352:4 + (set_global $assembly/buddy/base_ptr + ;;@ assembly/buddy.ts:352:15 + (block (result i32) + (set_global $assembly/buddy/max_ptr + ;;@ assembly/buddy.ts:352:25 + (get_global $assembly/buddy/SPLIT_END) + ) + (get_global $assembly/buddy/max_ptr) + ) + ) + ;;@ assembly/buddy.ts:353:4 + (set_global $assembly/buddy/bucket_limit + ;;@ assembly/buddy.ts:353:19 + (i32.const 27) + ) + ;;@ assembly/buddy.ts:354:4 + (drop + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:354:19 + (i32.add + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:354:30 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:355:4 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:355:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:355:25 + (i32.const 27) + ) + ) + ;;@ assembly/buddy.ts:356:4 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:356:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:356:25 + (i32.const 27) + ) + ;;@ assembly/buddy.ts:356:44 + (get_global $assembly/buddy/base_ptr) + ) + ) + ) + ;;@ assembly/buddy.ts:364:2 + (set_local $4 + ;;@ assembly/buddy.ts:363:2 + (tee_local $1 + ;;@ assembly/buddy.ts:363:11 + (call $assembly/buddy/bucket_for_request + ;;@ assembly/buddy.ts:363:30 + (i32.add + (get_local $0) + ;;@ assembly/buddy.ts:363:40 + (i32.const 8) + ) + ) + ) + ) + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:371:9 + (i32.add + (get_local $1) + ;;@ assembly/buddy.ts:371:18 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:371:26 + (block + ;;@ assembly/buddy.ts:379:4 + (if + ;;@ assembly/buddy.ts:379:8 + (i32.eqz + ;;@ assembly/buddy.ts:379:9 + (call $assembly/buddy/lower_bucket_limit + ;;@ assembly/buddy.ts:379:28 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:380:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:388:4 + (if + ;;@ assembly/buddy.ts:388:8 + (i32.eqz + ;;@ assembly/buddy.ts:387:4 + (tee_local $2 + ;;@ assembly/buddy.ts:387:10 + (call $assembly/buddy/list_pop + ;;@ assembly/buddy.ts:387:37 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:387:48 + (get_local $1) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:388:14 + (block + ;;@ assembly/buddy.ts:393:6 + (if + ;;@ assembly/buddy.ts:393:10 + (i32.and + (if (result i32) + (tee_local $2 + (i32.ne + (get_local $1) + ;;@ assembly/buddy.ts:393:20 + (get_global $assembly/buddy/bucket_limit) + ) + ) + (get_local $2) + (i32.eqz + ;;@ assembly/buddy.ts:393:36 + (get_local $1) + ) + ) + (i32.const 1) + ) + ;;@ assembly/buddy.ts:393:49 + (block + ;;@ assembly/buddy.ts:394:8 + (set_local $1 + (i32.sub + (get_local $1) + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:395:8 + (br $continue|0) + ) + ) + ;;@ assembly/buddy.ts:405:6 + (if + ;;@ assembly/buddy.ts:405:10 + (i32.eqz + ;;@ assembly/buddy.ts:405:11 + (call $assembly/buddy/lower_bucket_limit + ;;@ assembly/buddy.ts:405:30 + (i32.sub + (get_local $1) + ;;@ assembly/buddy.ts:405:39 + (i32.const 1) + ) + ) + ) + ;;@ assembly/buddy.ts:406:15 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:408:6 + (set_local $2 + ;;@ assembly/buddy.ts:408:12 + (call $assembly/buddy/list_pop + ;;@ assembly/buddy.ts:408:39 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:408:50 + (get_local $1) + ) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:415:4 + (set_local $3 + ;;@ assembly/buddy.ts:415:11 + (i32.shl + (i32.const 1) + ;;@ assembly/buddy.ts:415:16 + (i32.sub + ;;@ assembly/buddy.ts:415:17 + (i32.const 31) + ;;@ assembly/buddy.ts:415:34 + (get_local $1) + ) + ) + ) + ;;@ assembly/buddy.ts:417:4 + (if + ;;@ assembly/buddy.ts:417:8 + (i32.eqz + ;;@ assembly/buddy.ts:417:9 + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:417:24 + (i32.add + (get_local $2) + ;;@ assembly/buddy.ts:416:19 + (if (result i32) + (i32.lt_u + (get_local $1) + ;;@ assembly/buddy.ts:416:28 + (get_local $4) + ) + ;;@ assembly/buddy.ts:416:46 + (i32.add + (i32.div_u + (get_local $3) + ;;@ assembly/buddy.ts:416:53 + (i32.const 2) + ) + ;;@ assembly/buddy.ts:416:57 + (i32.const 8) + ) + ;;@ assembly/buddy.ts:416:69 + (get_local $3) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:417:45 + (block + ;;@ assembly/buddy.ts:418:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:418:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:418:27 + (get_local $1) + ) + ;;@ assembly/buddy.ts:418:36 + (get_local $2) + ) + ;;@ assembly/buddy.ts:419:13 + (return + (i32.const 0) + ) + ) + ) + ;;@ assembly/buddy.ts:434:4 + (if + ;;@ assembly/buddy.ts:433:4 + (tee_local $3 + ;;@ assembly/buddy.ts:433:8 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:433:21 + (get_local $2) + ;;@ assembly/buddy.ts:433:26 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:435:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:435:27 + (get_local $3) + ) + ) + (loop $continue|1 + (if + ;;@ assembly/buddy.ts:445:11 + (i32.lt_u + (get_local $1) + ;;@ assembly/buddy.ts:445:20 + (get_local $4) + ) + (block + ;;@ assembly/buddy.ts:448:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:446:6 + (tee_local $3 + ;;@ assembly/buddy.ts:446:10 + (i32.add + (i32.mul + (get_local $3) + ;;@ assembly/buddy.ts:446:14 + (i32.const 2) + ) + ;;@ assembly/buddy.ts:446:18 + (i32.const 1) + ) + ) + ) + ;;@ assembly/buddy.ts:449:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:449:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:447:6 + (tee_local $1 + (i32.add + (get_local $1) + (i32.const 1) + ) + ) + ) + ;;@ assembly/buddy.ts:449:36 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:449:66 + (i32.add + (get_local $3) + ;;@ assembly/buddy.ts:449:70 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:449:73 + (get_local $1) + ) + ) + (br $continue|1) + ) + ) + ) + ;;@ assembly/buddy.ts:456:4 + (i32.store + ;;@ assembly/buddy.ts:456:17 + (get_local $2) + ;;@ assembly/buddy.ts:456:22 + (get_local $0) + ) + ;;@ assembly/buddy.ts:457:17 + (return + ;;@ assembly/buddy.ts:457:11 + (i32.add + (get_local $2) + ;;@ assembly/buddy.ts:457:17 + (i32.const 8) + ) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:460:9 + (i32.const 0) + ) + (func $assembly/buddy/free_memory (; 14 ;) (type $iv) (param $0 i32) + (local $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:470:2 + (if + ;;@ assembly/buddy.ts:470:6 + (i32.eqz + ;;@ assembly/buddy.ts:470:7 + (get_local $0) + ) + ;;@ assembly/buddy.ts:471:4 + (return) + ) + ;;@ assembly/buddy.ts:480:2 + (set_local $1 + ;;@ assembly/buddy.ts:480:11 + (call $assembly/buddy/bucket_for_request + ;;@ assembly/buddy.ts:480:30 + (i32.add + (i32.load + ;;@ assembly/buddy.ts:479:2 + (tee_local $0 + ;;@ assembly/buddy.ts:479:8 + (i32.sub + (get_local $0) + ;;@ assembly/buddy.ts:479:14 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:480:49 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:481:2 + (set_local $0 + ;;@ assembly/buddy.ts:481:6 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:481:19 + (get_local $0) + ;;@ assembly/buddy.ts:481:24 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:487:2 + (block $break|0 + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:487:9 + (get_local $0) + (block + ;;@ assembly/buddy.ts:494:4 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:494:25 + (get_local $0) + ) + ;;@ assembly/buddy.ts:505:6 + (br_if $break|0 + ;;@ assembly/buddy.ts:504:8 + (if (result i32) + (tee_local $2 + (call $assembly/buddy/parent_is_split + ;;@ assembly/buddy.ts:504:24 + (get_local $0) + ) + ) + (get_local $2) + ;;@ assembly/buddy.ts:504:30 + (i32.eq + (get_local $1) + ;;@ assembly/buddy.ts:504:40 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ;;@ assembly/buddy.ts:515:4 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:515:16 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:515:46 + (i32.add + (i32.xor + ;;@ assembly/buddy.ts:515:47 + (i32.sub + ;;@ assembly/buddy.ts:515:48 + (get_local $0) + ;;@ assembly/buddy.ts:515:52 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:57 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:62 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:65 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:516:4 + (set_local $0 + ;;@ assembly/buddy.ts:516:8 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:516:9 + (get_local $0) + ;;@ assembly/buddy.ts:516:13 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:516:18 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:517:4 + (set_local $1 + (i32.sub + (get_local $1) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:526:2 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:526:12 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:526:23 + (get_local $1) + ) + ;;@ assembly/buddy.ts:526:32 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:526:62 + (get_local $0) + ;;@ assembly/buddy.ts:526:65 + (get_local $1) + ) + ) + ) + (func $start (; 15 ;) (type $v) + (set_global $assembly/buddy/BUCKET_START + ;;@ assembly/buddy.ts:92:26 + (get_global $HEAP_BASE) + ) + (set_global $assembly/buddy/BUCKET_END + ;;@ assembly/buddy.ts:93:24 + (i32.add + (get_global $assembly/buddy/BUCKET_START) + (i32.const 112) + ) + ) + (set_global $assembly/buddy/SPLIT_START + ;;@ assembly/buddy.ts:143:25 + (get_global $assembly/buddy/BUCKET_END) + ) + (set_global $assembly/buddy/SPLIT_END + ;;@ assembly/buddy.ts:144:23 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + (i32.const 16777216) + ) + ) + ) +) diff --git a/tests/allocators/buddy/buddy.untouched.wat b/tests/allocators/buddy/buddy.untouched.wat new file mode 100644 index 00000000..9b8e83ac --- /dev/null +++ b/tests/allocators/buddy/buddy.untouched.wat @@ -0,0 +1,1317 @@ +(module + (type $i (func (result i32))) + (type $ii (func (param i32) (result i32))) + (type $iiiiv (func (param i32 i32 i32 i32))) + (type $iv (func (param i32))) + (type $iiv (func (param i32 i32))) + (type $iii (func (param i32 i32) (result i32))) + (type $v (func)) + (import "env" "abort" (func $abort (param i32 i32 i32 i32))) + (global $assembly/buddy/HEADER_SIZE i32 (i32.const 8)) + (global $assembly/buddy/MIN_ALLOC_LOG2 i32 (i32.const 4)) + (global $assembly/buddy/MIN_ALLOC i32 (i32.const 16)) + (global $assembly/buddy/MAX_ALLOC_LOG2 i32 (i32.const 31)) + (global $assembly/buddy/MAX_ALLOC i32 (i32.const -2147483648)) + (global $assembly/buddy/BUCKET_COUNT i32 (i32.const 28)) + (global $assembly/buddy/BUCKET_START (mut i32) (i32.const 0)) + (global $assembly/buddy/BUCKET_END (mut i32) (i32.const 0)) + (global $assembly/buddy/bucket_limit (mut i32) (i32.const 0)) + (global $assembly/buddy/SPLIT_COUNT i32 (i32.const 16777216)) + (global $assembly/buddy/SPLIT_START (mut i32) (i32.const 0)) + (global $assembly/buddy/SPLIT_END (mut i32) (i32.const 0)) + (global $assembly/buddy/base_ptr (mut i32) (i32.const 0)) + (global $assembly/buddy/max_ptr (mut i32) (i32.const 0)) + (global $HEAP_BASE i32 (i32.const 44)) + (memory $0 1) + (data (i32.const 4) "\11\00\00\00a\00s\00s\00e\00m\00b\00l\00y\00/\00b\00u\00d\00d\00y\00.\00t\00s\00") + (export "allocate_memory" (func $assembly/buddy/allocate_memory)) + (export "free_memory" (func $assembly/buddy/free_memory)) + (export "memory" (memory $0)) + (start $start) + (func $assembly/buddy/update_max_ptr (; 1 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + ;;@ assembly/buddy.ts:175:2 + (if + ;;@ assembly/buddy.ts:175:6 + (i32.gt_u + (get_local $0) + ;;@ assembly/buddy.ts:175:18 + (get_global $assembly/buddy/max_ptr) + ) + ;;@ assembly/buddy.ts:175:27 + (block + ;;@ assembly/buddy.ts:176:4 + (set_local $1 + ;;@ assembly/buddy.ts:176:16 + (i32.shr_u + (i32.and + ;;@ assembly/buddy.ts:176:17 + (i32.add + ;;@ assembly/buddy.ts:176:18 + (i32.sub + ;;@ assembly/buddy.ts:176:19 + (get_local $0) + ;;@ assembly/buddy.ts:176:31 + (get_global $assembly/buddy/max_ptr) + ) + ;;@ assembly/buddy.ts:176:42 + (i32.const 65535) + ) + ;;@ assembly/buddy.ts:176:52 + (i32.xor + ;;@ assembly/buddy.ts:176:53 + (i32.const 65535) + (i32.const -1) + ) + ) + ;;@ assembly/buddy.ts:176:65 + (i32.const 16) + ) + ) + ;;@ assembly/buddy.ts:177:4 + (if + ;;@ assembly/buddy.ts:177:8 + (i32.lt_s + (grow_memory + ;;@ assembly/buddy.ts:177:20 + (get_local $1) + ) + ;;@ assembly/buddy.ts:177:29 + (i32.const 0) + ) + ;;@ assembly/buddy.ts:178:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:180:4 + (set_global $assembly/buddy/max_ptr + ;;@ assembly/buddy.ts:180:14 + (get_local $0) + ) + ) + ) + ;;@ assembly/buddy.ts:182:9 + (return + (i32.const 1) + ) + ) + (func $assembly/buddy/get_bucket (; 2 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:96:2 + (if + (i32.eqz + ;;@ assembly/buddy.ts:96:9 + (i32.lt_u + (get_local $0) + ;;@ assembly/buddy.ts:96:17 + (i32.const 28) + ) + ) + (block + (call $abort + (i32.const 0) + (i32.const 4) + (i32.const 96) + (i32.const 2) + ) + (unreachable) + ) + ) + ;;@ assembly/buddy.ts:97:58 + (return + ;;@ assembly/buddy.ts:97:9 + (i32.load + ;;@ assembly/buddy.ts:97:20 + (i32.add + (get_global $assembly/buddy/BUCKET_START) + ;;@ assembly/buddy.ts:97:35 + (i32.mul + (get_local $0) + ;;@ assembly/buddy.ts:97:43 + (i32.const 4) + ) + ) + ) + ) + ) + (func $assembly/buddy/list_init (; 3 ;) (type $iv) (param $0 i32) + ;;@ assembly/buddy.ts:191:2 + (i32.store + (get_local $0) + ;;@ assembly/buddy.ts:191:14 + (get_local $0) + ) + ;;@ assembly/buddy.ts:192:2 + (i32.store offset=4 + (get_local $0) + ;;@ assembly/buddy.ts:192:14 + (get_local $0) + ) + ) + (func $assembly/buddy/list_push (; 4 ;) (type $iiv) (param $0 i32) (param $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:200:2 + (set_local $2 + ;;@ assembly/buddy.ts:200:13 + (i32.load + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:201:2 + (i32.store + (get_local $1) + ;;@ assembly/buddy.ts:201:15 + (get_local $2) + ) + ;;@ assembly/buddy.ts:202:2 + (i32.store offset=4 + (get_local $1) + ;;@ assembly/buddy.ts:202:15 + (get_local $0) + ) + ;;@ assembly/buddy.ts:203:2 + (i32.store offset=4 + (get_local $2) + ;;@ assembly/buddy.ts:203:14 + (get_local $1) + ) + ;;@ assembly/buddy.ts:204:2 + (i32.store + (get_local $0) + ;;@ assembly/buddy.ts:204:14 + (get_local $1) + ) + ) + (func $assembly/buddy/bucket_for_request (; 5 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:271:2 + (set_local $1 + ;;@ assembly/buddy.ts:271:15 + (i32.sub + (i32.const 28) + ;;@ assembly/buddy.ts:271:30 + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:272:2 + (set_local $2 + ;;@ assembly/buddy.ts:272:13 + (i32.const 16) + ) + ;;@ assembly/buddy.ts:274:2 + (block $break|0 + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:274:9 + (i32.lt_u + (get_local $2) + ;;@ assembly/buddy.ts:274:16 + (get_local $0) + ) + (block + (block + ;;@ assembly/buddy.ts:275:4 + (set_local $1 + (i32.sub + (get_local $1) + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:276:4 + (set_local $2 + (i32.mul + (get_local $2) + ;;@ assembly/buddy.ts:276:12 + (i32.const 2) + ) + ) + ) + (br $continue|0) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:279:9 + (return + (get_local $1) + ) + ) + (func $assembly/buddy/node_for_ptr (; 6 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + ;;@ assembly/buddy.ts:246:75 + (return + ;;@ assembly/buddy.ts:246:9 + (i32.sub + (i32.add + (i32.shr_u + ;;@ assembly/buddy.ts:246:10 + (i32.sub + ;;@ assembly/buddy.ts:246:11 + (get_local $0) + ;;@ assembly/buddy.ts:246:17 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:246:30 + (i32.sub + ;;@ assembly/buddy.ts:246:31 + (i32.const 31) + ;;@ assembly/buddy.ts:246:48 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:246:59 + (i32.shl + ;;@ assembly/buddy.ts:246:60 + (i32.const 1) + ;;@ assembly/buddy.ts:246:65 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:246:75 + (i32.const 1) + ) + ) + ) + (func $assembly/buddy/node_is_split (; 7 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:147:2 + (if + (i32.eqz + ;;@ assembly/buddy.ts:147:9 + (i32.lt_u + (get_local $0) + ;;@ assembly/buddy.ts:147:17 + (i32.const 16777216) + ) + ) + (block + (call $abort + (i32.const 0) + (i32.const 4) + (i32.const 147) + (i32.const 2) + ) + (unreachable) + ) + ) + ;;@ assembly/buddy.ts:148:37 + (return + ;;@ assembly/buddy.ts:148:9 + (i32.load8_u + ;;@ assembly/buddy.ts:148:18 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + ;;@ assembly/buddy.ts:148:32 + (get_local $0) + ) + ) + ) + ) + (func $assembly/buddy/parent_is_split (; 8 ;) (type $ii) (param $0 i32) (result i32) + ;;@ assembly/buddy.ts:253:2 + (set_local $0 + ;;@ assembly/buddy.ts:253:10 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:253:11 + (get_local $0) + ;;@ assembly/buddy.ts:253:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:253:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:254:58 + (return + ;;@ assembly/buddy.ts:254:9 + (i32.and + (i32.shr_s + ;;@ assembly/buddy.ts:254:10 + (call $assembly/buddy/node_is_split + ;;@ assembly/buddy.ts:254:24 + (i32.div_u + (get_local $0) + ;;@ assembly/buddy.ts:254:32 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:254:38 + (i32.rem_u + ;;@ assembly/buddy.ts:254:44 + (get_local $0) + ;;@ assembly/buddy.ts:254:52 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:254:58 + (i32.const 1) + ) + ) + ) + (func $assembly/buddy/list_remove (; 9 ;) (type $iv) (param $0 i32) + (local $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:214:2 + (set_local $1 + ;;@ assembly/buddy.ts:214:13 + (i32.load + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:215:2 + (set_local $2 + ;;@ assembly/buddy.ts:215:13 + (i32.load offset=4 + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:216:2 + (i32.store offset=4 + (get_local $1) + ;;@ assembly/buddy.ts:216:14 + (get_local $2) + ) + ;;@ assembly/buddy.ts:217:2 + (i32.store + (get_local $2) + ;;@ assembly/buddy.ts:217:14 + (get_local $1) + ) + ) + (func $assembly/buddy/ptr_for_node (; 10 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + ;;@ assembly/buddy.ts:237:77 + (return + ;;@ assembly/buddy.ts:237:9 + (i32.add + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:237:20 + (i32.shl + ;;@ assembly/buddy.ts:237:21 + (i32.add + ;;@ assembly/buddy.ts:237:22 + (i32.sub + (get_local $0) + ;;@ assembly/buddy.ts:237:30 + (i32.shl + ;;@ assembly/buddy.ts:237:31 + (i32.const 1) + ;;@ assembly/buddy.ts:237:36 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:237:46 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:237:52 + (i32.sub + ;;@ assembly/buddy.ts:237:53 + (i32.const 31) + ;;@ assembly/buddy.ts:237:70 + (get_local $1) + ) + ) + ) + ) + ) + (func $assembly/buddy/node_set_split (; 11 ;) (type $iiv) (param $0 i32) (param $1 i32) + ;;@ assembly/buddy.ts:152:2 + (if + (i32.eqz + ;;@ assembly/buddy.ts:152:9 + (i32.lt_u + (get_local $0) + ;;@ assembly/buddy.ts:152:17 + (i32.const 16777216) + ) + ) + (block + (call $abort + (i32.const 0) + (i32.const 4) + (i32.const 152) + (i32.const 2) + ) + (unreachable) + ) + ) + ;;@ assembly/buddy.ts:153:2 + (i32.store8 + ;;@ assembly/buddy.ts:153:12 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + ;;@ assembly/buddy.ts:153:26 + (get_local $0) + ) + ;;@ assembly/buddy.ts:153:33 + (get_local $1) + ) + ) + (func $assembly/buddy/flip_parent_is_split (; 12 ;) (type $iv) (param $0 i32) + (local $1 i32) + ;;@ assembly/buddy.ts:261:2 + (set_local $0 + ;;@ assembly/buddy.ts:261:10 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:261:11 + (get_local $0) + ;;@ assembly/buddy.ts:261:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:261:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:262:2 + (set_local $1 + ;;@ assembly/buddy.ts:262:18 + (i32.div_u + (get_local $0) + ;;@ assembly/buddy.ts:262:26 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:263:2 + (call $assembly/buddy/node_set_split + ;;@ assembly/buddy.ts:263:17 + (get_local $1) + ;;@ assembly/buddy.ts:263:28 + (i32.xor + (call $assembly/buddy/node_is_split + ;;@ assembly/buddy.ts:263:42 + (get_local $1) + ) + ;;@ assembly/buddy.ts:263:55 + (i32.shl + ;;@ assembly/buddy.ts:263:61 + (i32.const 1) + ;;@ assembly/buddy.ts:263:66 + (i32.rem_u + ;;@ assembly/buddy.ts:263:67 + (get_local $0) + ;;@ assembly/buddy.ts:263:75 + (i32.const 8) + ) + ) + ) + ) + ) + (func $assembly/buddy/lower_bucket_limit (; 13 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + ;;@ assembly/buddy.ts:288:2 + (block $break|0 + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:288:9 + (i32.lt_u + (get_local $0) + ;;@ assembly/buddy.ts:288:18 + (get_global $assembly/buddy/bucket_limit) + ) + (block + (block + ;;@ assembly/buddy.ts:289:4 + (set_local $1 + ;;@ assembly/buddy.ts:289:15 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:289:28 + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:289:38 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ;;@ assembly/buddy.ts:290:4 + (nop) + ;;@ assembly/buddy.ts:298:4 + (if + ;;@ assembly/buddy.ts:298:8 + (i32.eqz + ;;@ assembly/buddy.ts:298:9 + (call $assembly/buddy/parent_is_split + ;;@ assembly/buddy.ts:298:25 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:298:32 + (block + ;;@ assembly/buddy.ts:299:6 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:299:18 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:300:6 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:300:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:300:27 + (block (result i32) + (set_global $assembly/buddy/bucket_limit + (i32.sub + ;;@ assembly/buddy.ts:300:29 + (get_global $assembly/buddy/bucket_limit) + (i32.const 1) + ) + ) + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ;;@ assembly/buddy.ts:301:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:301:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:301:27 + (get_global $assembly/buddy/bucket_limit) + ) + ;;@ assembly/buddy.ts:301:42 + (get_global $assembly/buddy/base_ptr) + ) + ;;@ assembly/buddy.ts:302:6 + (br $continue|0) + ) + ) + ;;@ assembly/buddy.ts:313:4 + (set_local $2 + ;;@ assembly/buddy.ts:313:18 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:313:31 + (i32.add + (get_local $1) + ;;@ assembly/buddy.ts:313:38 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:313:41 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ;;@ assembly/buddy.ts:314:4 + (if + ;;@ assembly/buddy.ts:314:8 + (i32.eqz + ;;@ assembly/buddy.ts:314:9 + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:314:24 + (i32.add + (get_local $2) + ;;@ assembly/buddy.ts:314:38 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:315:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:317:4 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:317:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:317:25 + (get_global $assembly/buddy/bucket_limit) + ) + ;;@ assembly/buddy.ts:317:40 + (get_local $2) + ) + ;;@ assembly/buddy.ts:318:4 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:318:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:318:25 + (block (result i32) + (set_global $assembly/buddy/bucket_limit + (i32.sub + ;;@ assembly/buddy.ts:318:27 + (get_global $assembly/buddy/bucket_limit) + (i32.const 1) + ) + ) + (get_global $assembly/buddy/bucket_limit) + ) + ) + ) + ;;@ assembly/buddy.ts:324:4 + (set_local $1 + ;;@ assembly/buddy.ts:324:11 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:324:12 + (get_local $1) + ;;@ assembly/buddy.ts:324:19 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:324:24 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:325:4 + (if + ;;@ assembly/buddy.ts:325:8 + (i32.ne + (get_local $1) + ;;@ assembly/buddy.ts:325:16 + (i32.const 0) + ) + ;;@ assembly/buddy.ts:326:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:326:27 + (get_local $1) + ) + ) + ) + (br $continue|0) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:330:9 + (return + (i32.const 1) + ) + ) + (func $assembly/buddy/list_pop (; 14 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + ;;@ assembly/buddy.ts:224:2 + (set_local $1 + ;;@ assembly/buddy.ts:224:13 + (i32.load + (get_local $0) + ) + ) + ;;@ assembly/buddy.ts:225:2 + (if + ;;@ assembly/buddy.ts:225:6 + (i32.eq + (get_local $1) + ;;@ assembly/buddy.ts:225:14 + (get_local $0) + ) + ;;@ assembly/buddy.ts:225:27 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:226:2 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:226:14 + (get_local $1) + ) + ;;@ assembly/buddy.ts:227:9 + (return + (get_local $1) + ) + ) + (func $assembly/buddy/allocate_memory (; 15 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + ;;@ assembly/buddy.ts:335:2 + (nop) + ;;@ assembly/buddy.ts:342:2 + (if + ;;@ assembly/buddy.ts:342:6 + (i32.gt_u + (i32.add + (get_local $0) + ;;@ assembly/buddy.ts:342:16 + (i32.const 8) + ) + ;;@ assembly/buddy.ts:342:30 + (i32.const -2147483648) + ) + ;;@ assembly/buddy.ts:343:11 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:351:2 + (if + ;;@ assembly/buddy.ts:351:6 + (i32.eq + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:351:18 + (i32.const 0) + ) + ;;@ assembly/buddy.ts:351:21 + (block + ;;@ assembly/buddy.ts:352:4 + (set_global $assembly/buddy/base_ptr + ;;@ assembly/buddy.ts:352:15 + (block (result i32) + (set_global $assembly/buddy/max_ptr + ;;@ assembly/buddy.ts:352:25 + (get_global $assembly/buddy/SPLIT_END) + ) + (get_global $assembly/buddy/max_ptr) + ) + ) + ;;@ assembly/buddy.ts:353:4 + (set_global $assembly/buddy/bucket_limit + ;;@ assembly/buddy.ts:353:19 + (i32.sub + (i32.const 28) + ;;@ assembly/buddy.ts:353:34 + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:354:4 + (drop + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:354:19 + (i32.add + (get_global $assembly/buddy/base_ptr) + ;;@ assembly/buddy.ts:354:30 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:355:4 + (call $assembly/buddy/list_init + ;;@ assembly/buddy.ts:355:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:355:25 + (i32.sub + (i32.const 28) + ;;@ assembly/buddy.ts:355:40 + (i32.const 1) + ) + ) + ) + ;;@ assembly/buddy.ts:356:4 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:356:14 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:356:25 + (i32.sub + (i32.const 28) + ;;@ assembly/buddy.ts:356:40 + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:356:44 + (get_global $assembly/buddy/base_ptr) + ) + ) + ) + ;;@ assembly/buddy.ts:363:2 + (set_local $2 + ;;@ assembly/buddy.ts:363:11 + (call $assembly/buddy/bucket_for_request + ;;@ assembly/buddy.ts:363:30 + (i32.add + (get_local $0) + ;;@ assembly/buddy.ts:363:40 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:364:2 + (set_local $1 + ;;@ assembly/buddy.ts:364:20 + (get_local $2) + ) + ;;@ assembly/buddy.ts:371:2 + (block $break|0 + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:371:9 + (i32.ne + (i32.add + (get_local $2) + ;;@ assembly/buddy.ts:371:18 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:371:23 + (i32.const 0) + ) + (block + (block + ;;@ assembly/buddy.ts:372:4 + (nop) + ;;@ assembly/buddy.ts:373:4 + (nop) + ;;@ assembly/buddy.ts:379:4 + (if + ;;@ assembly/buddy.ts:379:8 + (i32.eqz + ;;@ assembly/buddy.ts:379:9 + (call $assembly/buddy/lower_bucket_limit + ;;@ assembly/buddy.ts:379:28 + (get_local $2) + ) + ) + ;;@ assembly/buddy.ts:380:13 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:387:4 + (set_local $6 + ;;@ assembly/buddy.ts:387:10 + (call $assembly/buddy/list_pop + ;;@ assembly/buddy.ts:387:37 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:387:48 + (get_local $2) + ) + ) + ) + ;;@ assembly/buddy.ts:388:4 + (if + ;;@ assembly/buddy.ts:388:8 + (i32.eqz + ;;@ assembly/buddy.ts:388:9 + (get_local $6) + ) + ;;@ assembly/buddy.ts:388:14 + (block + ;;@ assembly/buddy.ts:393:6 + (if + ;;@ assembly/buddy.ts:393:10 + (i32.and + (if (result i32) + (i32.ne + (tee_local $7 + (i32.ne + (get_local $2) + ;;@ assembly/buddy.ts:393:20 + (get_global $assembly/buddy/bucket_limit) + ) + ) + (i32.const 0) + ) + (get_local $7) + ;;@ assembly/buddy.ts:393:36 + (i32.eq + (get_local $2) + ;;@ assembly/buddy.ts:393:46 + (i32.const 0) + ) + ) + (i32.const 1) + ) + ;;@ assembly/buddy.ts:393:49 + (block + ;;@ assembly/buddy.ts:394:8 + (set_local $2 + (i32.sub + (get_local $2) + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:395:8 + (br $continue|0) + ) + ) + ;;@ assembly/buddy.ts:405:6 + (if + ;;@ assembly/buddy.ts:405:10 + (i32.eqz + ;;@ assembly/buddy.ts:405:11 + (call $assembly/buddy/lower_bucket_limit + ;;@ assembly/buddy.ts:405:30 + (i32.sub + (get_local $2) + ;;@ assembly/buddy.ts:405:39 + (i32.const 1) + ) + ) + ) + ;;@ assembly/buddy.ts:406:15 + (return + (i32.const 0) + ) + ) + ;;@ assembly/buddy.ts:408:6 + (set_local $6 + ;;@ assembly/buddy.ts:408:12 + (call $assembly/buddy/list_pop + ;;@ assembly/buddy.ts:408:39 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:408:50 + (get_local $2) + ) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:415:4 + (set_local $3 + ;;@ assembly/buddy.ts:415:11 + (i32.shl + (i32.const 1) + ;;@ assembly/buddy.ts:415:16 + (i32.sub + ;;@ assembly/buddy.ts:415:17 + (i32.const 31) + ;;@ assembly/buddy.ts:415:34 + (get_local $2) + ) + ) + ) + ;;@ assembly/buddy.ts:416:4 + (set_local $4 + ;;@ assembly/buddy.ts:416:19 + (if (result i32) + (i32.lt_u + (get_local $2) + ;;@ assembly/buddy.ts:416:28 + (get_local $1) + ) + ;;@ assembly/buddy.ts:416:46 + (i32.add + (i32.div_u + (get_local $3) + ;;@ assembly/buddy.ts:416:53 + (i32.const 2) + ) + ;;@ assembly/buddy.ts:416:57 + (i32.const 8) + ) + ;;@ assembly/buddy.ts:416:69 + (get_local $3) + ) + ) + ;;@ assembly/buddy.ts:417:4 + (if + ;;@ assembly/buddy.ts:417:8 + (i32.eqz + ;;@ assembly/buddy.ts:417:9 + (call $assembly/buddy/update_max_ptr + ;;@ assembly/buddy.ts:417:24 + (i32.add + (get_local $6) + ;;@ assembly/buddy.ts:417:30 + (get_local $4) + ) + ) + ) + ;;@ assembly/buddy.ts:417:45 + (block + ;;@ assembly/buddy.ts:418:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:418:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:418:27 + (get_local $2) + ) + ;;@ assembly/buddy.ts:418:36 + (get_local $6) + ) + ;;@ assembly/buddy.ts:419:13 + (return + (i32.const 0) + ) + ) + ) + ;;@ assembly/buddy.ts:433:4 + (set_local $5 + ;;@ assembly/buddy.ts:433:8 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:433:21 + (get_local $6) + ;;@ assembly/buddy.ts:433:26 + (get_local $2) + ) + ) + ;;@ assembly/buddy.ts:434:4 + (if + ;;@ assembly/buddy.ts:434:8 + (i32.ne + (get_local $5) + ;;@ assembly/buddy.ts:434:13 + (i32.const 0) + ) + ;;@ assembly/buddy.ts:435:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:435:27 + (get_local $5) + ) + ) + ;;@ assembly/buddy.ts:445:4 + (block $break|1 + (loop $continue|1 + (if + ;;@ assembly/buddy.ts:445:11 + (i32.lt_u + (get_local $2) + ;;@ assembly/buddy.ts:445:20 + (get_local $1) + ) + (block + (block + ;;@ assembly/buddy.ts:446:6 + (set_local $5 + ;;@ assembly/buddy.ts:446:10 + (i32.add + (i32.mul + (get_local $5) + ;;@ assembly/buddy.ts:446:14 + (i32.const 2) + ) + ;;@ assembly/buddy.ts:446:18 + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:447:6 + (set_local $2 + (i32.add + (get_local $2) + (i32.const 1) + ) + ) + ;;@ assembly/buddy.ts:448:6 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:448:27 + (get_local $5) + ) + ;;@ assembly/buddy.ts:449:6 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:449:16 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:449:27 + (get_local $2) + ) + ;;@ assembly/buddy.ts:449:36 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:449:66 + (i32.add + (get_local $5) + ;;@ assembly/buddy.ts:449:70 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:449:73 + (get_local $2) + ) + ) + ) + (br $continue|1) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:456:4 + (i32.store + ;;@ assembly/buddy.ts:456:17 + (get_local $6) + ;;@ assembly/buddy.ts:456:22 + (get_local $0) + ) + ;;@ assembly/buddy.ts:457:17 + (return + ;;@ assembly/buddy.ts:457:11 + (i32.add + (get_local $6) + ;;@ assembly/buddy.ts:457:17 + (i32.const 8) + ) + ) + ) + (br $continue|0) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:460:9 + (return + (i32.const 0) + ) + ) + (func $assembly/buddy/free_memory (; 16 ;) (type $iv) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + ;;@ assembly/buddy.ts:465:2 + (nop) + ;;@ assembly/buddy.ts:470:2 + (if + ;;@ assembly/buddy.ts:470:6 + (i32.eqz + ;;@ assembly/buddy.ts:470:7 + (get_local $0) + ) + ;;@ assembly/buddy.ts:471:4 + (return) + ) + ;;@ assembly/buddy.ts:479:2 + (set_local $0 + ;;@ assembly/buddy.ts:479:8 + (i32.sub + (get_local $0) + ;;@ assembly/buddy.ts:479:14 + (i32.const 8) + ) + ) + ;;@ assembly/buddy.ts:480:2 + (set_local $1 + ;;@ assembly/buddy.ts:480:11 + (call $assembly/buddy/bucket_for_request + ;;@ assembly/buddy.ts:480:30 + (i32.add + (i32.load + ;;@ assembly/buddy.ts:480:42 + (get_local $0) + ) + ;;@ assembly/buddy.ts:480:49 + (i32.const 8) + ) + ) + ) + ;;@ assembly/buddy.ts:481:2 + (set_local $2 + ;;@ assembly/buddy.ts:481:6 + (call $assembly/buddy/node_for_ptr + ;;@ assembly/buddy.ts:481:19 + (get_local $0) + ;;@ assembly/buddy.ts:481:24 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:487:2 + (block $break|0 + (loop $continue|0 + (if + ;;@ assembly/buddy.ts:487:9 + (i32.ne + (get_local $2) + ;;@ assembly/buddy.ts:487:14 + (i32.const 0) + ) + (block + (block + ;;@ assembly/buddy.ts:494:4 + (call $assembly/buddy/flip_parent_is_split + ;;@ assembly/buddy.ts:494:25 + (get_local $2) + ) + ;;@ assembly/buddy.ts:504:4 + (if + ;;@ assembly/buddy.ts:504:8 + (if (result i32) + (i32.ne + (tee_local $3 + (call $assembly/buddy/parent_is_split + ;;@ assembly/buddy.ts:504:24 + (get_local $2) + ) + ) + (i32.const 0) + ) + (get_local $3) + ;;@ assembly/buddy.ts:504:30 + (i32.eq + (get_local $1) + ;;@ assembly/buddy.ts:504:40 + (get_global $assembly/buddy/bucket_limit) + ) + ) + ;;@ assembly/buddy.ts:505:6 + (br $break|0) + ) + ;;@ assembly/buddy.ts:515:4 + (call $assembly/buddy/list_remove + ;;@ assembly/buddy.ts:515:16 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:515:46 + (i32.add + (i32.xor + ;;@ assembly/buddy.ts:515:47 + (i32.sub + ;;@ assembly/buddy.ts:515:48 + (get_local $2) + ;;@ assembly/buddy.ts:515:52 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:57 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:62 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:515:65 + (get_local $1) + ) + ) + ;;@ assembly/buddy.ts:516:4 + (set_local $2 + ;;@ assembly/buddy.ts:516:8 + (i32.div_u + (i32.sub + ;;@ assembly/buddy.ts:516:9 + (get_local $2) + ;;@ assembly/buddy.ts:516:13 + (i32.const 1) + ) + ;;@ assembly/buddy.ts:516:18 + (i32.const 2) + ) + ) + ;;@ assembly/buddy.ts:517:4 + (set_local $1 + (i32.sub + (get_local $1) + (i32.const 1) + ) + ) + ) + (br $continue|0) + ) + ) + ) + ) + ;;@ assembly/buddy.ts:526:2 + (call $assembly/buddy/list_push + ;;@ assembly/buddy.ts:526:12 + (call $assembly/buddy/get_bucket + ;;@ assembly/buddy.ts:526:23 + (get_local $1) + ) + ;;@ assembly/buddy.ts:526:32 + (call $assembly/buddy/ptr_for_node + ;;@ assembly/buddy.ts:526:62 + (get_local $2) + ;;@ assembly/buddy.ts:526:65 + (get_local $1) + ) + ) + ) + (func $start (; 17 ;) (type $v) + (set_global $assembly/buddy/BUCKET_START + ;;@ assembly/buddy.ts:92:26 + (get_global $HEAP_BASE) + ) + (set_global $assembly/buddy/BUCKET_END + ;;@ assembly/buddy.ts:93:24 + (i32.add + (get_global $assembly/buddy/BUCKET_START) + ;;@ assembly/buddy.ts:93:39 + (i32.mul + (i32.const 28) + ;;@ assembly/buddy.ts:93:54 + (i32.const 4) + ) + ) + ) + (set_global $assembly/buddy/SPLIT_START + ;;@ assembly/buddy.ts:143:25 + (get_global $assembly/buddy/BUCKET_END) + ) + (set_global $assembly/buddy/SPLIT_END + ;;@ assembly/buddy.ts:144:23 + (i32.add + (get_global $assembly/buddy/SPLIT_START) + ;;@ assembly/buddy.ts:144:37 + (i32.mul + (i32.const 16777216) + ;;@ assembly/buddy.ts:144:51 + (i32.const 1) + ) + ) + ) + ) +) diff --git a/tests/allocators/buddy/index.js b/tests/allocators/buddy/index.js new file mode 100644 index 00000000..0f7eedc7 --- /dev/null +++ b/tests/allocators/buddy/index.js @@ -0,0 +1,30 @@ +const fs = require("fs"); + +function test(file) { + console.log("Testing '" + file + "' ...\n"); + + const exports = new WebAssembly.Instance(WebAssembly.Module(fs.readFileSync(__dirname + "/" + file)), { + env: { + abort: function(msg, file, line, column) { + throw Error("Assertion failed: " + (msg ? "'" + getString(msg) + "' " : "") + "at " + getString(file) + ":" + line + ":" + column); + }, + log: function(ptr) { console.log(getString(ptr)); }, + logi: function(i) { console.log(i); } + } + }).exports; + + function getString(ptr) { + var len = new Uint32Array(exports.memory.buffer, ptr)[0]; + var str = new Uint16Array(exports.memory.buffer, ptr + 4).subarray(0, len); + return String.fromCharCode.apply(String, str); + } + + var ptr = exports.allocate_memory(16); + exports.free_memory(ptr); + // runner(exports, 20, 20000); // picked so I/O isn't the bottleneck + console.log("mem final: " + exports.memory.buffer.byteLength); + console.log(); +} + +test("buddy.untouched.wasm"); +test("buddy.optimized.wasm"); diff --git a/tests/allocators/buddy/package.json b/tests/allocators/buddy/package.json new file mode 100644 index 00000000..e459396e --- /dev/null +++ b/tests/allocators/buddy/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "scripts": { + "build": "npm run build:untouched && npm run build:optimized", + "build:untouched": "asc assembly/index.ts -t buddy.untouched.wat -b buddy.untouched.wasm --validate --sourceMap --measure", + "build:optimized": "asc assembly/index.ts -t buddy.optimized.wat -b buddy.optimized.wasm --validate --sourceMap --measure --noDebug --noAssert --optimize", + "test": "node ./index" + } +} diff --git a/tests/tlsf/assembly/index.ts b/tests/allocators/tlsf/assembly/index.ts similarity index 100% rename from tests/tlsf/assembly/index.ts rename to tests/allocators/tlsf/assembly/index.ts diff --git a/tests/tlsf/assembly/tsconfig.json b/tests/allocators/tlsf/assembly/tsconfig.json similarity index 100% rename from tests/tlsf/assembly/tsconfig.json rename to tests/allocators/tlsf/assembly/tsconfig.json diff --git a/tests/tlsf/forever.js b/tests/allocators/tlsf/forever.js similarity index 100% rename from tests/tlsf/forever.js rename to tests/allocators/tlsf/forever.js diff --git a/tests/tlsf/index.js b/tests/allocators/tlsf/index.js similarity index 100% rename from tests/tlsf/index.js rename to tests/allocators/tlsf/index.js diff --git a/tests/tlsf/package.json b/tests/allocators/tlsf/package.json similarity index 100% rename from tests/tlsf/package.json rename to tests/allocators/tlsf/package.json diff --git a/tests/tlsf/runner.js b/tests/allocators/tlsf/runner.js similarity index 100% rename from tests/tlsf/runner.js rename to tests/allocators/tlsf/runner.js diff --git a/tests/tlsf/tlsf.optimized.wat b/tests/allocators/tlsf/tlsf.optimized.wat similarity index 100% rename from tests/tlsf/tlsf.optimized.wat rename to tests/allocators/tlsf/tlsf.optimized.wat diff --git a/tests/tlsf/tlsf.untouched.wat b/tests/allocators/tlsf/tlsf.untouched.wat similarity index 100% rename from tests/tlsf/tlsf.untouched.wat rename to tests/allocators/tlsf/tlsf.untouched.wat