mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-01 09:52:19 +00:00
More TLSF
This commit is contained in:
parent
867e037ff0
commit
7d5e56cef5
@ -167,6 +167,12 @@ else if (args.trapMode !== "allow") {
|
|||||||
if (args.optimize)
|
if (args.optimize)
|
||||||
module.optimize();
|
module.optimize();
|
||||||
|
|
||||||
|
if (args.runPasses) {
|
||||||
|
if (typeof args.runPasses === "string")
|
||||||
|
args.runPasses = args.runPasses.split(",");
|
||||||
|
module.runPasses(args.runPasses.map(pass => pass.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
var hasOutput = false;
|
var hasOutput = false;
|
||||||
|
|
||||||
if (args.outFile != null) {
|
if (args.outFile != null) {
|
||||||
|
@ -64,5 +64,9 @@
|
|||||||
],
|
],
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "allow"
|
"default": "allow"
|
||||||
|
},
|
||||||
|
"runPasses": {
|
||||||
|
"desc": "Specifies additional Binaryen passes to run.",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
examples/tlsf/assembly/LICENSE
Normal file
36
examples/tlsf/assembly/LICENSE
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
tlsf.ts is based on:
|
||||||
|
|
||||||
|
Two Level Segregated Fit memory allocator, version 3.1.
|
||||||
|
Written by Matthew Conte
|
||||||
|
http://tlsf.baisoku.org
|
||||||
|
|
||||||
|
Based on the original documentation by Miguel Masmano:
|
||||||
|
http://www.gii.upv.es/tlsf/main/docs
|
||||||
|
|
||||||
|
This implementation was written to the specification
|
||||||
|
of the document, therefore no GPL restrictions apply.
|
||||||
|
|
||||||
|
Copyright (c) 2006-2016, Matthew Conte
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,16 +1,20 @@
|
|||||||
// based on https://github.com/mattconte/tlsf (BSD)
|
// based on https://github.com/mattconte/tlsf (BSD, see LICENSE file)
|
||||||
|
|
||||||
type size_t = u32;
|
// Configuration
|
||||||
|
|
||||||
// TLSF achieves O(1) cost for malloc and free operations by limiting
|
const SL_INDEX_COUNT_LOG2: u32 = 5;
|
||||||
// the search for a free block to a free list of guaranteed size
|
|
||||||
// adequate to fulfill the request, combined with efficient free list
|
// Internal constants
|
||||||
// queries using bitmasks and architecture-specific bit-manipulation
|
|
||||||
// routines.
|
const ALIGN_SIZE_LOG2: u32 = sizeof<usize>() == 8 ? 3 : 2;
|
||||||
//
|
const ALIGN_SIZE: u32 = 1 << ALIGN_SIZE_LOG2;
|
||||||
// NOTE: TLSF spec relies on ffs/fls returning values 0..31 with -1
|
const FL_INDEX_MAX: u32 = sizeof<usize>() == 8 ? 32 : 30;
|
||||||
// indicating that no bits are set. In WebAssembly, ctz and clz return
|
const SL_INDEX_COUNT: u32 = 1 << SL_INDEX_COUNT_LOG2;
|
||||||
// 32/64 if no bits are set.
|
const FL_INDEX_SHIFT: u32 = SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2;
|
||||||
|
const FL_INDEX_COUNT: u32 = FL_INDEX_MAX - FL_INDEX_SHIFT + 1;
|
||||||
|
const SMALL_BLOCK_SIZE: u32 = 1 << FL_INDEX_SHIFT;
|
||||||
|
|
||||||
|
// WebAssembly-specific ffs/fls
|
||||||
|
|
||||||
function ffs<T>(word: i32): i32 {
|
function ffs<T>(word: i32): i32 {
|
||||||
return word ? <i32>ctz(word) : -1;
|
return word ? <i32>ctz(word) : -1;
|
||||||
@ -20,67 +24,44 @@ function fls<T>(word: T): i32 {
|
|||||||
return (<i32>sizeof<T>() << 3) - <i32>clz(word) - 1;
|
return (<i32>sizeof<T>() << 3) - <i32>clz(word) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SL_INDEX_COUNT_LOG2: u32 = 5;
|
/** Block header structure. */
|
||||||
|
|
||||||
const ALIGN_SIZE_LOG2: u32 = sizeof<size_t>() == 8 ? 3 : 2;
|
|
||||||
const ALIGN_SIZE: u32 = (1 << ALIGN_SIZE_LOG2);
|
|
||||||
const FL_INDEX_MAX: u32 = sizeof<size_t>() == 8 ? 32 : 30;
|
|
||||||
const SL_INDEX_COUNT: u32 = (1 << SL_INDEX_COUNT_LOG2);
|
|
||||||
const FL_INDEX_SHIFT: u32 = SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2;
|
|
||||||
const FL_INDEX_COUNT: u32 = FL_INDEX_MAX - FL_INDEX_SHIFT + 1;
|
|
||||||
const SMALL_BLOCK_SIZE: u32 = 1 << FL_INDEX_SHIFT;
|
|
||||||
|
|
||||||
assert(sizeof<u32>() * 8 >= SL_INDEX_COUNT);
|
|
||||||
assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block header structure.
|
|
||||||
*
|
|
||||||
* There are several implementation subtleties involved:
|
|
||||||
* - The prev_phys_block field is only valid if the previous block is free.
|
|
||||||
* - The prev_phys_block field is actually stored at the end of the
|
|
||||||
* previous block. It appears at the beginning of this structure only to
|
|
||||||
* simplify the implementation.
|
|
||||||
* - The next_free / prev_free fields are only valid if the block is free.
|
|
||||||
*/
|
|
||||||
@explicit
|
@explicit
|
||||||
class block_header_t {
|
class BlockHeader {
|
||||||
|
/** Points to the previous physical block. Only valid if the previous block
|
||||||
/* Points to the previous physical block. */
|
* is free. Actually stored at the end of the previous block. */
|
||||||
prev_phys_block: block_header_t;
|
prev_phys_block: BlockHeader;
|
||||||
/* The size of this block, excluding the block header. */
|
/** The size of this block, excluding the block header. The two least
|
||||||
size: size_t;
|
* significant bits are used to store the block status. */
|
||||||
/* Next free block. */
|
tagged_size: usize;
|
||||||
next_free: block_header_t;
|
/** Next free block. Only valid if the block is free. */
|
||||||
/* Previous free block. */
|
next_free: BlockHeader;
|
||||||
prev_free: block_header_t;
|
/** Previous free block. Only valid if the block is free. */
|
||||||
|
prev_free: BlockHeader;
|
||||||
}
|
}
|
||||||
const sizeof_block_header_t: usize = 3 * sizeof<usize>() * sizeof<size_t>();
|
|
||||||
|
const sizeof_block_header_t: usize = 4 * sizeof<usize>();
|
||||||
|
|
||||||
// Since block sizes are always at least a multiple of 4, the two least
|
// Since block sizes are always at least a multiple of 4, the two least
|
||||||
// significant bits of the size field are used to store the block status:
|
// significant bits of the size field are used to store the block status.
|
||||||
// - bit 0: whether block is busy or free
|
const block_header_free_bit: usize = 1 << 0;
|
||||||
// - bit 1: whether previous block is busy or free
|
const block_header_prev_free_bit: usize = 1 << 1;
|
||||||
const block_header_free_bit: size_t = 1 << 0;
|
|
||||||
const block_header_prev_free_bit: size_t = 1 << 1;
|
|
||||||
|
|
||||||
// The size of the block header exposed to used blocks is the size field.
|
// The size of the block header exposed to used blocks is the size field.
|
||||||
// The prev_phys_block field is stored *inside* the previous free block.
|
// The prev_phys_block field is stored *inside* the previous free block.
|
||||||
const block_header_overhead: size_t = sizeof<size_t>();
|
const block_header_overhead: usize = sizeof<usize>();
|
||||||
|
|
||||||
// User data starts directly after the size field in a used block.
|
// User data starts directly after the size field in a used block.
|
||||||
const block_start_offset: size_t = sizeof<usize>() + sizeof<size_t>();
|
const block_start_offset: usize = sizeof<usize>() + sizeof<usize>();
|
||||||
|
|
||||||
// A free block must be large enough to store its header minus the size of
|
// A free block must be large enough to store its header minus the size of
|
||||||
// the prev_phys_block field, and no larger than the number of addressable
|
// the prev_phys_block field, and no larger than the number of addressable
|
||||||
// bits for FL_INDEX.
|
// bits for FL_INDEX.
|
||||||
const block_size_min: size_t = sizeof_block_header_t - sizeof<usize>();
|
const block_size_min: usize = sizeof_block_header_t - sizeof<usize>();
|
||||||
const block_size_max: size_t = 1 << FL_INDEX_MAX;
|
const block_size_max: usize = <usize>1 << FL_INDEX_MAX;
|
||||||
|
|
||||||
/* The TLSF control structure. */
|
/* The TLSF control structure. */
|
||||||
@explicit
|
@explicit
|
||||||
class control_t extends block_header_t { // Empty lists point at this block to indicate they are free.
|
class Control extends BlockHeader { // Empty lists point at this block to indicate they are free.
|
||||||
|
|
||||||
/* First level free list bitmap. */
|
/* First level free list bitmap. */
|
||||||
fl_bitmap: u32;
|
fl_bitmap: u32;
|
||||||
/** Second level free list bitmaps. */
|
/** Second level free list bitmaps. */
|
||||||
@ -93,115 +74,113 @@ class control_t extends block_header_t { // Empty lists point at this block to i
|
|||||||
return store<u32>(changetype<usize>(this) + offset + fl_index * sizeof<u32>(), sl_map);
|
return store<u32>(changetype<usize>(this) + offset + fl_index * sizeof<u32>(), sl_map);
|
||||||
}
|
}
|
||||||
/** Head of free lists. */
|
/** Head of free lists. */
|
||||||
blocks(fl_index: u32, sl_index: u32): block_header_t {
|
blocks(fl_index: u32, sl_index: u32): BlockHeader {
|
||||||
const offset: usize = sizeof_block_header_t + sizeof<u32>() * FL_INDEX_COUNT * sizeof<u32>();
|
const offset: usize = sizeof_block_header_t + sizeof<u32>() + FL_INDEX_COUNT * sizeof<u32>();
|
||||||
return load<block_header_t>(changetype<usize>(this) + offset + (fl_index * SL_INDEX_COUNT + sl_index) * sizeof<usize>());
|
return load<BlockHeader>(changetype<usize>(this) + offset + (fl_index * SL_INDEX_COUNT + sl_index) * sizeof<usize>());
|
||||||
}
|
}
|
||||||
blocks_set(fl_index: u32, sl_index: u32, block: block_header_t): void {
|
blocks_set(fl_index: u32, sl_index: u32, block: BlockHeader): void {
|
||||||
const offset: usize = sizeof_block_header_t + sizeof<u32>() * FL_INDEX_COUNT * sizeof<u32>();
|
const offset: usize = sizeof_block_header_t + sizeof<u32>() + FL_INDEX_COUNT * sizeof<u32>();
|
||||||
return store<block_header_t>(changetype<usize>(this) + offset + (fl_index * SL_INDEX_COUNT + sl_index) * sizeof<usize>(), block);
|
return store<BlockHeader>(changetype<usize>(this) + offset + (fl_index * SL_INDEX_COUNT + sl_index) * sizeof<usize>(), block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizeof_control_t: usize = sizeof_block_header_t + (1 + FL_INDEX_COUNT) * sizeof<u32>() + FL_INDEX_COUNT * SL_INDEX_COUNT * sizeof<usize>();
|
const sizeof_control_t: usize = sizeof_block_header_t + (1 + FL_INDEX_COUNT) * sizeof<u32>() + FL_INDEX_COUNT * SL_INDEX_COUNT * sizeof<usize>();
|
||||||
|
|
||||||
// block_header_t member functions.
|
function block_size(block: BlockHeader): usize {
|
||||||
|
return block.tagged_size & ~(block_header_free_bit | block_header_prev_free_bit);
|
||||||
function block_size(block: block_header_t): size_t {
|
|
||||||
return block.size & ~(block_header_free_bit | block_header_prev_free_bit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_set_size(block: block_header_t, size: size_t): void {
|
function block_set_size(block: BlockHeader, size: usize): void {
|
||||||
var oldsize = block.size;
|
block.tagged_size = size | (block.tagged_size & (block_header_free_bit | block_header_prev_free_bit));
|
||||||
block.size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_is_last(block: block_header_t): bool {
|
function block_is_last(block: BlockHeader): bool {
|
||||||
return block_size(block) == 0;
|
return block_size(block) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_is_free(block: block_header_t): bool {
|
function block_is_free(block: BlockHeader): bool {
|
||||||
return (block.size & block_header_free_bit) == block_header_free_bit;
|
return (block.tagged_size & block_header_free_bit) == block_header_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_set_free(block: block_header_t): void {
|
function block_set_free(block: BlockHeader): void {
|
||||||
block.size |= block_header_free_bit;
|
block.tagged_size |= block_header_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_set_used(block: block_header_t): void {
|
function block_set_used(block: BlockHeader): void {
|
||||||
block.size &= ~block_header_free_bit;
|
block.tagged_size &= ~block_header_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_is_prev_free(block: block_header_t): bool {
|
function block_is_prev_free(block: BlockHeader): bool {
|
||||||
return (block.size & block_header_prev_free_bit) == block_header_prev_free_bit;
|
return (block.tagged_size & block_header_prev_free_bit) == block_header_prev_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_set_prev_free(block: block_header_t): void {
|
function block_set_prev_free(block: BlockHeader): void {
|
||||||
block.size |= block_header_prev_free_bit;
|
block.tagged_size |= block_header_prev_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_set_prev_used(block: block_header_t): void {
|
function block_set_prev_used(block: BlockHeader): void {
|
||||||
block.size &= ~block_header_prev_free_bit;
|
block.tagged_size &= ~block_header_prev_free_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_from_ptr(ptr: usize): block_header_t {
|
function block_from_ptr(ptr: usize): BlockHeader {
|
||||||
return changetype<block_header_t>(ptr - block_start_offset);
|
return changetype<BlockHeader>(ptr - block_start_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_to_ptr(block: block_header_t): usize {
|
function block_to_ptr(block: BlockHeader): usize {
|
||||||
return changetype<usize>(block) + block_start_offset;
|
return changetype<usize>(block) + block_start_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return location of next block after block of given size. */
|
/* Return location of next block after block of given size. */
|
||||||
function offset_to_block(ptr: usize, size: size_t): block_header_t {
|
function offset_to_block(ptr: usize, size: usize): BlockHeader {
|
||||||
return changetype<block_header_t>(ptr + size);
|
return changetype<BlockHeader>(ptr + <usize>size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return location of previous block. */
|
/* Return location of previous block. */
|
||||||
function block_prev(block: block_header_t): block_header_t {
|
function block_prev(block: BlockHeader): BlockHeader {
|
||||||
assert(block_is_prev_free(block), "previous block must be free");
|
assert(block_is_prev_free(block), "previous block must be free");
|
||||||
return block.prev_phys_block;
|
return block.prev_phys_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return location of next existing block. */
|
/* Return location of next existing block. */
|
||||||
function block_next(block: block_header_t): block_header_t {
|
function block_next(block: BlockHeader): BlockHeader {
|
||||||
var next = offset_to_block(block_to_ptr(block), block_size(block) - block_header_overhead);
|
var next = offset_to_block(block_to_ptr(block), block_size(block) - block_header_overhead);
|
||||||
assert(!block_is_last(block));
|
assert(!block_is_last(block), "last block has no next block");
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link a new block with its physical neighbor, return the neighbor. */
|
/* Link a new block with its physical neighbor, return the neighbor. */
|
||||||
function block_link_next(block: block_header_t): block_header_t {
|
function block_link_next(block: BlockHeader): BlockHeader {
|
||||||
var next = block_next(block);
|
var next = block_next(block);
|
||||||
next.prev_phys_block = block;
|
next.prev_phys_block = block;
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_mark_as_free(block: block_header_t): void {
|
function block_mark_as_free(block: BlockHeader): void {
|
||||||
// Link the block to the next block, first.
|
// Link the block to the next block, first.
|
||||||
var next = block_link_next(block);
|
var next = block_link_next(block);
|
||||||
block_set_prev_free(next);
|
block_set_prev_free(next);
|
||||||
block_set_free(block);
|
block_set_free(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_mark_as_used(block: block_header_t): void {
|
function block_mark_as_used(block: BlockHeader): void {
|
||||||
var next = block_next(block);
|
var next = block_next(block);
|
||||||
block_set_prev_used(next);
|
block_set_prev_used(next);
|
||||||
block_set_used(block);
|
block_set_used(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
function align_up(x: size_t, align: size_t): size_t {
|
function align_up(x: usize, align: usize): usize {
|
||||||
assert(0 == (align & (align - 1)), "must align to a power of two");
|
assert(!(align & (align - 1)), "must align to a power of two");
|
||||||
return (x + (align - 1)) & ~(align - 1);
|
return (x + (align - 1)) & ~(align - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function align_down(x: size_t, align: size_t): size_t {
|
function align_down(x: usize, align: usize): usize {
|
||||||
assert(0 == (align & (align - 1)), "must align to a power of two");
|
assert(!(align & (align - 1)), "must align to a power of two");
|
||||||
return x - (x & (align - 1));
|
return x - (x & (align - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function align_ptr(ptr: usize, align: size_t): usize {
|
function align_ptr(ptr: usize, align: usize): usize {
|
||||||
var aligned = (ptr + (align - 1)) & ~(align - 1);
|
var aligned = (ptr + (align - 1)) & ~(align - 1);
|
||||||
assert(0 == (align & (align - 1)), "must align to a power of two");
|
assert(!(align & (align - 1)), "must align to a power of two");
|
||||||
return aligned;
|
return aligned;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,15 +188,12 @@ function align_ptr(ptr: usize, align: size_t): usize {
|
|||||||
* Adjust an allocation size to be aligned to word size, and no smaller
|
* Adjust an allocation size to be aligned to word size, and no smaller
|
||||||
* than internal minimum.
|
* than internal minimum.
|
||||||
*/
|
*/
|
||||||
function adjust_request_size(size: size_t, align: size_t): size_t {
|
function adjust_request_size(size: usize, align: usize): usize {
|
||||||
var adjust: size_t = 0;
|
var adjust: usize = 0;
|
||||||
if (size) {
|
if (size && size < block_size_max) {
|
||||||
var aligned = align_up(size, align);
|
var aligned = align_up(size, align);
|
||||||
// aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap
|
|
||||||
if (aligned < block_size_max) {
|
|
||||||
adjust = max(aligned, block_size_min);
|
adjust = max(aligned, block_size_min);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return adjust;
|
return adjust;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,14 +202,14 @@ function adjust_request_size(size: size_t, align: size_t): size_t {
|
|||||||
|
|
||||||
var fl_out: i32, sl_out: i32;
|
var fl_out: i32, sl_out: i32;
|
||||||
|
|
||||||
function mapping_insert(size: size_t): void {
|
function mapping_insert(size: usize): void {
|
||||||
var fl: i32, sl: i32;
|
var fl: i32, sl: i32;
|
||||||
if (size < SMALL_BLOCK_SIZE) {
|
if (size < SMALL_BLOCK_SIZE) {
|
||||||
// Store small blocks in first list.
|
// Store small blocks in first list.
|
||||||
fl = 0;
|
fl = 0;
|
||||||
sl = <i32>size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
|
sl = <i32>size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
|
||||||
} else {
|
} else {
|
||||||
fl = fls<size_t>(size);
|
fl = fls<usize>(size);
|
||||||
sl = (<i32>size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
|
sl = (<i32>size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
|
||||||
fl -= (FL_INDEX_SHIFT - 1);
|
fl -= (FL_INDEX_SHIFT - 1);
|
||||||
}
|
}
|
||||||
@ -241,35 +217,38 @@ function mapping_insert(size: size_t): void {
|
|||||||
sl_out = sl;
|
sl_out = sl;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapping_search(size: size_t): void {
|
function mapping_search(size: usize): void {
|
||||||
if (size >= SMALL_BLOCK_SIZE) {
|
if (size >= SMALL_BLOCK_SIZE) {
|
||||||
var round: size_t = (1 << (fls<size_t>(size) - SL_INDEX_COUNT_LOG2)) - 1;
|
var round: usize = (<usize>1 << (fls<usize>(size) - SL_INDEX_COUNT_LOG2)) - 1;
|
||||||
size += round;
|
size += round;
|
||||||
}
|
}
|
||||||
mapping_insert(size);
|
mapping_insert(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
function search_suitable_block(control: control_t, fl: i32, sl: i32): block_header_t {
|
function search_suitable_block(control: Control, fl: i32, sl: i32): BlockHeader {
|
||||||
|
// First, search for a block in the list associated with the given
|
||||||
|
// fl/sl index.
|
||||||
var sl_map = control.sl_bitmap(fl) & (<u32>~0 << sl);
|
var sl_map = control.sl_bitmap(fl) & (<u32>~0 << sl);
|
||||||
if (!sl_map) {
|
if (!sl_map) {
|
||||||
// No block exists. Search in the next largest first-level list.
|
// No block exists. Search in the next largest first-level list.
|
||||||
var fl_map = control.fl_bitmap & (<u32>~0 << (fl + 1));
|
var fl_map = control.fl_bitmap & (<u32>~0 << (fl + 1));
|
||||||
if (!fl_map) {
|
if (!fl_map) {
|
||||||
// No free blocks available, memory has been exhausted.
|
// No free blocks available, memory has been exhausted.
|
||||||
return changetype<block_header_t>(0);
|
return changetype<BlockHeader>(0);
|
||||||
}
|
}
|
||||||
fl = ffs<u32>(fl_map);
|
fl = ffs<u32>(fl_map);
|
||||||
fl_out = fl;
|
fl_out = fl;
|
||||||
sl_map = control.sl_bitmap(fl);
|
sl_map = control.sl_bitmap(fl);
|
||||||
}
|
}
|
||||||
assert(sl_map, "internal error - second level bitmap is null");
|
assert(sl_map, "internal error - second level bitmap is null");
|
||||||
|
|
||||||
sl = ffs<u32>(sl_map);
|
sl = ffs<u32>(sl_map);
|
||||||
sl_out = sl;
|
sl_out = sl;
|
||||||
|
// Return the first block in the free list.
|
||||||
return control.blocks(fl, sl);
|
return control.blocks(fl, sl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_free_block(control: control_t, block: block_header_t, fl: i32, sl: i32): void {
|
/* Remove a free block from the free list.*/
|
||||||
|
function remove_free_block(control: Control, block: BlockHeader, fl: i32, sl: i32): void {
|
||||||
var prev = block.prev_free;
|
var prev = block.prev_free;
|
||||||
var next = block.next_free;
|
var next = block.next_free;
|
||||||
assert(prev, "prev_free field can not be null");
|
assert(prev, "prev_free field can not be null");
|
||||||
@ -288,47 +267,49 @@ function remove_free_block(control: control_t, block: block_header_t, fl: i32, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert_free_block(control: control_t, block: block_header_t, fl: i32, sl: i32): void {
|
/* Insert a free block into the free block list. */
|
||||||
|
function insert_free_block(control: Control, block: BlockHeader, fl: i32, sl: i32): void {
|
||||||
var current = control.blocks(fl, sl);
|
var current = control.blocks(fl, sl);
|
||||||
|
|
||||||
assert(current, "free list cannot have a null entry");
|
assert(current, "free list cannot have a null entry");
|
||||||
assert(block, "cannot insert a null entry into the free list");
|
assert(block, "cannot insert a null entry into the free list");
|
||||||
|
|
||||||
block.next_free = current;
|
block.next_free = current;
|
||||||
block.prev_free = control;
|
block.prev_free = control;
|
||||||
current.prev_free = block;
|
current.prev_free = block;
|
||||||
|
|
||||||
assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE), "block not aligned properly");
|
assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE), "block not aligned properly");
|
||||||
|
|
||||||
|
// Insert the new block at the head of the list, and mark the first-
|
||||||
|
// and second-level bitmaps appropriately.
|
||||||
control.blocks_set(fl, sl, block);
|
control.blocks_set(fl, sl, block);
|
||||||
control.fl_bitmap |= (1 << fl);
|
control.fl_bitmap |= (1 << fl);
|
||||||
control.sl_bitmap_set(fl, control.sl_bitmap(fl) | (1 << sl))
|
control.sl_bitmap_set(fl, control.sl_bitmap(fl) | (1 << sl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove a given block from the free list. */
|
||||||
function block_remove(control: control_t, block: block_header_t): void {
|
function block_remove(control: Control, block: BlockHeader): void {
|
||||||
mapping_insert(block_size(block));
|
mapping_insert(block_size(block));
|
||||||
remove_free_block(control, block, fl_out, sl_out);
|
remove_free_block(control, block, fl_out, sl_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_insert(control: control_t, block: block_header_t): void {
|
/* Insert a given block into the free list. */
|
||||||
|
function block_insert(control: Control, block: BlockHeader): void {
|
||||||
mapping_insert(block_size(block));
|
mapping_insert(block_size(block));
|
||||||
insert_free_block(control, block, fl_out, sl_out);
|
insert_free_block(control, block, fl_out, sl_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_can_split(block: block_header_t, size: size_t): bool {
|
function block_can_split(block: BlockHeader, size: usize): bool {
|
||||||
return block_size(block) >= sizeof_block_header_t + size;
|
return block_size(block) >= sizeof_block_header_t + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_split(block: block_header_t, size: size_t): block_header_t {
|
/* Split a block into two, the second of which is free. */
|
||||||
|
function block_split(block: BlockHeader, size: usize): BlockHeader {
|
||||||
|
// Calculate the amount of space left in the remaining block.
|
||||||
var remaining = offset_to_block(block_to_ptr(block), size - block_header_overhead);
|
var remaining = offset_to_block(block_to_ptr(block), size - block_header_overhead);
|
||||||
var remain_size = block_size(block) - (size + block_header_overhead);
|
var remain_size = block_size(block) - (size + block_header_overhead);
|
||||||
|
|
||||||
assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE), "remaining block not aligned properly");
|
assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE), "remaining block not aligned properly");
|
||||||
assert(block_size(block) == remain_size + size + block_header_overhead);
|
assert(block_size(block) == remain_size + size + block_header_overhead);
|
||||||
|
|
||||||
block_set_size(remaining, remain_size);
|
block_set_size(remaining, remain_size);
|
||||||
|
|
||||||
assert(block_size(remaining) >= block_size_min, "block split with invalid size");
|
assert(block_size(remaining) >= block_size_min, "block split with invalid size");
|
||||||
|
|
||||||
block_set_size(block, size);
|
block_set_size(block, size);
|
||||||
@ -336,15 +317,17 @@ function block_split(block: block_header_t, size: size_t): block_header_t {
|
|||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_absorb(prev: block_header_t, block: block_header_t): block_header_t {
|
/* Absorb a free block's storage into an adjacent previous free block. */
|
||||||
|
function block_absorb(prev: BlockHeader, block: BlockHeader): BlockHeader {
|
||||||
assert(!block_is_last(prev), "previous block can't be last");
|
assert(!block_is_last(prev), "previous block can't be last");
|
||||||
prev.size += block_size(block) + block_header_overhead;
|
// Note: Leaves flags untouched.
|
||||||
|
prev.tagged_size += block_size(block) + block_header_overhead;
|
||||||
block_link_next(prev);
|
block_link_next(prev);
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Merge a just-freed block with an adjacent previous free block. */
|
/* Merge a just-freed block with an adjacent previous free block. */
|
||||||
function block_merge_prev(control: control_t, block: block_header_t): block_header_t {
|
function block_merge_prev(control: Control, block: BlockHeader): BlockHeader {
|
||||||
if (block_is_prev_free(block)) {
|
if (block_is_prev_free(block)) {
|
||||||
var prev = block_prev(block);
|
var prev = block_prev(block);
|
||||||
assert(prev, "prev physical block can't be null");
|
assert(prev, "prev physical block can't be null");
|
||||||
@ -356,8 +339,8 @@ function block_merge_prev(control: control_t, block: block_header_t): block_head
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Merge a just-freed block with an adjacent free block. */
|
/* Merge a just-freed block with an adjacent free block. */
|
||||||
function block_merge_next(control: control_t, block: block_header_t): block_header_t {
|
function block_merge_next(control: Control, block: BlockHeader): BlockHeader {
|
||||||
var next: block_header_t = block_next(block);
|
var next = block_next(block);
|
||||||
assert(next, "next physical block can't be null");
|
assert(next, "next physical block can't be null");
|
||||||
if (block_is_free(next)) {
|
if (block_is_free(next)) {
|
||||||
assert(!block_is_last(block), "previous block can't be last");
|
assert(!block_is_last(block), "previous block can't be last");
|
||||||
@ -368,7 +351,7 @@ function block_merge_next(control: control_t, block: block_header_t): block_head
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Trim any trailing block space off the end of a block, return to pool. */
|
/* Trim any trailing block space off the end of a block, return to pool. */
|
||||||
function block_trim_free(control: control_t, block: block_header_t, size: size_t): void {
|
function block_trim_free(control: Control, block: BlockHeader, size: usize): void {
|
||||||
assert(block_is_free(block), "block must be free");
|
assert(block_is_free(block), "block must be free");
|
||||||
if (block_can_split(block, size)) {
|
if (block_can_split(block, size)) {
|
||||||
var remaining_block = block_split(block, size);
|
var remaining_block = block_split(block, size);
|
||||||
@ -379,10 +362,10 @@ function block_trim_free(control: control_t, block: block_header_t, size: size_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Trim any trailing block space off the end of a used block, return to pool. */
|
/* Trim any trailing block space off the end of a used block, return to pool. */
|
||||||
function block_trim_used(control: control_t, block: block_header_t, size: size_t): void {
|
function block_trim_used(control: Control, block: BlockHeader, size: usize): void {
|
||||||
assert(!block_is_free(block), "block must be used");
|
assert(!block_is_free(block), "block must be used");
|
||||||
if (block_can_split(block, size)) {
|
if (block_can_split(block, size)) {
|
||||||
// If the next block is free, we must coalesce
|
// If the next block is free, we must coalesce.
|
||||||
var remaining_block = block_split(block, size);
|
var remaining_block = block_split(block, size);
|
||||||
block_set_prev_used(remaining_block);
|
block_set_prev_used(remaining_block);
|
||||||
remaining_block = block_merge_next(control, remaining_block);
|
remaining_block = block_merge_next(control, remaining_block);
|
||||||
@ -390,7 +373,7 @@ function block_trim_used(control: control_t, block: block_header_t, size: size_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_trim_free_leading(control: control_t, block: block_header_t, size: size_t): block_header_t {
|
function block_trim_free_leading(control: Control, block: BlockHeader, size: usize): BlockHeader {
|
||||||
var remaining_block = block;
|
var remaining_block = block;
|
||||||
if (block_can_split(block, size)) {
|
if (block_can_split(block, size)) {
|
||||||
remaining_block = block_split(block, size - block_header_overhead);
|
remaining_block = block_split(block, size - block_header_overhead);
|
||||||
@ -401,9 +384,9 @@ function block_trim_free_leading(control: control_t, block: block_header_t, size
|
|||||||
return remaining_block;
|
return remaining_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_locate_free(control: control_t, size: size_t): block_header_t {
|
function block_locate_free(control: Control, size: usize): BlockHeader {
|
||||||
var index: u64 = 0;
|
var index: u64 = 0;
|
||||||
var block: block_header_t = changetype<block_header_t>(0);
|
var block: BlockHeader = changetype<BlockHeader>(0);
|
||||||
if (size) {
|
if (size) {
|
||||||
mapping_search(size);
|
mapping_search(size);
|
||||||
if (fl_out < FL_INDEX_MAX) {
|
if (fl_out < FL_INDEX_MAX) {
|
||||||
@ -417,7 +400,7 @@ function block_locate_free(control: control_t, size: size_t): block_header_t {
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
function block_prepare_used(control: control_t, block: block_header_t, size: size_t): usize {
|
function block_prepare_used(control: Control, block: BlockHeader, size: usize): usize {
|
||||||
var p: usize = 0;
|
var p: usize = 0;
|
||||||
if (block) {
|
if (block) {
|
||||||
assert(size, "size must be non-zero");
|
assert(size, "size must be non-zero");
|
||||||
@ -429,7 +412,7 @@ function block_prepare_used(control: control_t, block: block_header_t, size: siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Clear structure and point all empty lists at the null block. */
|
/* Clear structure and point all empty lists at the null block. */
|
||||||
function control_construct(control: control_t): void {
|
function control_construct(control: Control): void {
|
||||||
control.next_free = control;
|
control.next_free = control;
|
||||||
control.prev_free = control;
|
control.prev_free = control;
|
||||||
control.fl_bitmap = 0;
|
control.fl_bitmap = 0;
|
||||||
@ -441,34 +424,39 @@ function control_construct(control: control_t): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type tlsf_t = usize;
|
var TLSF: usize = 0;
|
||||||
type pool_t = usize;
|
|
||||||
|
|
||||||
var TLSF: tlsf_t = 0;
|
function create(mem: usize): usize {
|
||||||
|
// Verify ffs/fls work properly
|
||||||
function create(mem: usize): tlsf_t {
|
assert(!test_ffs_fls());
|
||||||
|
// SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type
|
||||||
|
assert(sizeof<u32>() * 8 >= SL_INDEX_COUNT);
|
||||||
|
// Ensure we've properly tuned our sizes.
|
||||||
|
assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
|
||||||
if ((mem % ALIGN_SIZE) != 0)
|
if ((mem % ALIGN_SIZE) != 0)
|
||||||
throw new Error("Memory must be aligned");
|
throw new RangeError("Memory must be aligned");
|
||||||
control_construct(changetype<control_t>(mem));
|
control_construct(changetype<Control>(mem));
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_with_pool(mem: usize, bytes: size_t): tlsf_t {
|
function create_with_pool(mem: usize, bytes: usize): usize {
|
||||||
var tlsf = create(mem);
|
var tlsf = create(mem);
|
||||||
add_pool(tlsf, mem + sizeof_control_t, bytes - sizeof_control_t);
|
add_pool(tlsf, mem + sizeof_control_t, bytes - sizeof_control_t);
|
||||||
return tlsf;
|
return tlsf;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_pool(tlsf: tlsf_t, mem: usize, bytes: size_t): pool_t {
|
function add_pool(tlsf: usize, mem: usize, bytes: usize): usize {
|
||||||
var block: block_header_t;
|
var block: BlockHeader;
|
||||||
var next: block_header_t;
|
var next: BlockHeader;
|
||||||
|
|
||||||
const pool_overhead: size_t = 2 * block_header_overhead;
|
// Overhead of the TLSF structures in a given memory block, equal
|
||||||
|
// to the overhead of the free block and the sentinel block.
|
||||||
|
const pool_overhead: usize = 2 * block_header_overhead;
|
||||||
var pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
|
var pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
|
||||||
if ((mem % ALIGN_SIZE) != 0)
|
if ((mem % ALIGN_SIZE) != 0)
|
||||||
throw new Error("Memory must be aligned");
|
throw new RangeError("Memory must be aligned");
|
||||||
if (pool_bytes < block_size_min || pool_bytes > block_size_max)
|
if (pool_bytes < block_size_min || pool_bytes > block_size_max)
|
||||||
throw new Error("Memory size must be between min and max");
|
throw new RangeError("Memory size must be between min and max");
|
||||||
|
|
||||||
// Create the main free block. Offset the start of the block slightly
|
// Create the main free block. Offset the start of the block slightly
|
||||||
// so that the prev_phys_block field falls outside of the pool -
|
// so that the prev_phys_block field falls outside of the pool -
|
||||||
@ -477,7 +465,7 @@ function add_pool(tlsf: tlsf_t, mem: usize, bytes: size_t): pool_t {
|
|||||||
block_set_size(block, pool_bytes);
|
block_set_size(block, pool_bytes);
|
||||||
block_set_free(block);
|
block_set_free(block);
|
||||||
block_set_prev_used(block);
|
block_set_prev_used(block);
|
||||||
block_insert(changetype<control_t>(tlsf), block);
|
block_insert(changetype<Control>(tlsf), block);
|
||||||
|
|
||||||
// Split the block to create a zero-size sentinel block.
|
// Split the block to create a zero-size sentinel block.
|
||||||
next = block_link_next(block);
|
next = block_link_next(block);
|
||||||
@ -488,40 +476,9 @@ function add_pool(tlsf: tlsf_t, mem: usize, bytes: size_t): pool_t {
|
|||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function allocate_memory(size: size_t): usize {
|
// Tests
|
||||||
if (!TLSF)
|
|
||||||
TLSF = create_with_pool(HEAP_BASE, (current_memory() << 16) - HEAP_BASE);
|
|
||||||
var control = changetype<control_t>(TLSF);
|
|
||||||
var adjust = adjust_request_size(size, ALIGN_SIZE);
|
|
||||||
var block = block_locate_free(control, adjust);
|
|
||||||
if (!block && size > 0) {
|
|
||||||
if (size & 0xffff)
|
|
||||||
size = (size | 0xffff) + 1;
|
|
||||||
var oldsize = grow_memory(<u32>size >>> 16);
|
|
||||||
if (oldsize >= 0) {
|
|
||||||
add_pool(TLSF, <usize>oldsize << 16, size);
|
|
||||||
} else {
|
|
||||||
throw new Error("Out of memory");
|
|
||||||
}
|
|
||||||
block = block_locate_free(control, adjust);
|
|
||||||
}
|
|
||||||
return block_prepare_used(control, block, adjust);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function free_memory(ptr: usize): void {
|
|
||||||
if (TLSF && ptr) {
|
|
||||||
var control = changetype<control_t>(TLSF);
|
|
||||||
var block = block_from_ptr(ptr);
|
|
||||||
assert(!block_is_free(block), "block already marked as free");
|
|
||||||
block_mark_as_free(block);
|
|
||||||
block = block_merge_prev(control, block);
|
|
||||||
block = block_merge_next(control, block);
|
|
||||||
block_insert(control, block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_ffs_fls(): i32 {
|
function test_ffs_fls(): i32 {
|
||||||
// Verify ffs/fls work properly.
|
|
||||||
var rv = 0;
|
var rv = 0;
|
||||||
rv += (ffs<u32>(0) == -1) ? 0 : 0x1;
|
rv += (ffs<u32>(0) == -1) ? 0 : 0x1;
|
||||||
rv += (fls<u32>(0) == -1) ? 0 : 0x2;
|
rv += (fls<u32>(0) == -1) ? 0 : 0x2;
|
||||||
@ -537,4 +494,87 @@ function test_ffs_fls(): i32 {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!test_ffs_fls());
|
export function check(): i32 {
|
||||||
|
if (!TLSF)
|
||||||
|
TLSF = create_with_pool(HEAP_BASE, (current_memory() << 16) - HEAP_BASE);
|
||||||
|
var control = changetype<Control>(TLSF);
|
||||||
|
var status = 0;
|
||||||
|
for (var i = 0; i < FL_INDEX_COUNT; ++i) {
|
||||||
|
for (var j = 0; j < SL_INDEX_COUNT; ++j) {
|
||||||
|
var fl_map = control.fl_bitmap & (1 << i);
|
||||||
|
var sl_list = control.sl_bitmap(i);
|
||||||
|
var sl_map = sl_list & (1 << j);
|
||||||
|
var block = control.blocks(i, j);
|
||||||
|
if (!fl_map) {
|
||||||
|
if (!assert(!sl_map, "second-level map must be null"))
|
||||||
|
--status;
|
||||||
|
}
|
||||||
|
if (!sl_map) {
|
||||||
|
if (!assert(block == control, "block list must be null"))
|
||||||
|
--status;
|
||||||
|
} else {
|
||||||
|
if (!assert(sl_list, "no free blocks in second-level map"))
|
||||||
|
--status;
|
||||||
|
if (!assert(block != control, "block should not be null"))
|
||||||
|
--status;
|
||||||
|
while (block != control) {
|
||||||
|
if (!assert(block_is_free(block), "block should be free"))
|
||||||
|
--status;
|
||||||
|
if (!assert(!block_is_prev_free(block), "blocks should have coalesced"))
|
||||||
|
--status;
|
||||||
|
if (!assert(!block_is_free(block_next(block)), "blocks should have coalesced"))
|
||||||
|
--status;
|
||||||
|
if (!assert(block_is_prev_free(block_next(block)), "block should be free"))
|
||||||
|
--status;
|
||||||
|
if (!assert(block_size(block) >= block_size_min, "block not minimum size"))
|
||||||
|
--status;
|
||||||
|
mapping_insert(block_size(block));
|
||||||
|
if (!assert(fl_out == i && sl_out == j, "block size indexed in wrong list"))
|
||||||
|
--status;
|
||||||
|
block = block.next_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exported interface
|
||||||
|
|
||||||
|
function request_memory(size: usize): void {
|
||||||
|
// round size up to a full page
|
||||||
|
if (size & 0xffff)
|
||||||
|
size = (size | 0xffff) + 1;
|
||||||
|
// at least double memory for efficiency
|
||||||
|
var prev_pages = grow_memory(max<u32>(current_memory(), <u32>size >> 16));
|
||||||
|
if (prev_pages < 0) // out of host memory
|
||||||
|
unreachable();
|
||||||
|
var next_pages = current_memory();
|
||||||
|
add_pool(TLSF, <usize>prev_pages << 16, <usize>(next_pages - prev_pages) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function allocate_memory(size: usize): usize {
|
||||||
|
if (!TLSF) {
|
||||||
|
TLSF = create_with_pool(HEAP_BASE, (current_memory() << 16) - HEAP_BASE);
|
||||||
|
}
|
||||||
|
var control = changetype<Control>(TLSF);
|
||||||
|
var adjust = adjust_request_size(size, ALIGN_SIZE);
|
||||||
|
var block = block_locate_free(control, adjust);
|
||||||
|
if (!block && adjust > 0) {
|
||||||
|
request_memory(adjust);
|
||||||
|
block = block_locate_free(control, adjust);
|
||||||
|
}
|
||||||
|
return block_prepare_used(control, block, adjust);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function free_memory(ptr: usize): void {
|
||||||
|
if (TLSF && ptr) {
|
||||||
|
var control = changetype<Control>(TLSF);
|
||||||
|
var block = block_from_ptr(ptr);
|
||||||
|
assert(!block_is_free(block), "block already marked as free");
|
||||||
|
block_mark_as_free(block);
|
||||||
|
block = block_merge_prev(control, block);
|
||||||
|
block = block_merge_next(control, block);
|
||||||
|
block_insert(control, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:untouched && npm run build:optimized",
|
"build": "npm run build:untouched && npm run build:optimized",
|
||||||
"build:untouched": "asc assembly/tlsf.ts -t tlsf.untouched.wast -b tlsf.untouched.wasm --validate",
|
"build:untouched": "asc assembly/tlsf.ts -t tlsf.untouched.wast -b tlsf.untouched.wasm --validate",
|
||||||
"build:optimized": "asc -O assembly/tlsf.ts -b tlsf.optimized.wasm -t tlsf.optimized.wast --validate --noAssert",
|
"build:optimized": "asc -O assembly/tlsf.ts -b tlsf.optimized.wasm -t tlsf.optimized.wast --validate --noAssert --runPasses inlining",
|
||||||
"test": "node tests"
|
"test": "node tests"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,63 @@
|
|||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var tlsf = new WebAssembly.Instance(WebAssembly.Module(fs.readFileSync(__dirname + "/../tlsf.optimized.wasm")), {
|
|
||||||
|
function test(file) {
|
||||||
|
console.log("Testing '" + file + "' ...");
|
||||||
|
|
||||||
|
var tlsf = new WebAssembly.Instance(WebAssembly.Module(fs.readFileSync(__dirname + "/../" + file)), {
|
||||||
env: {
|
env: {
|
||||||
log_i: function(i) { i == -1 ? console.log("---") : console.log("log_i -> " + i); }
|
log_i: function(i) { i == -1 ? console.log("---") : console.log("log_i -> " + i); }
|
||||||
}
|
}
|
||||||
}).exports;
|
}).exports;
|
||||||
|
|
||||||
console.log(Object.keys(tlsf));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var memSize = 0;
|
var memSize = 0;
|
||||||
for (var j = 0; j < 500; ++j) {
|
var ptr = 0;
|
||||||
var ptr;
|
for (var j = 0; j < 10000; ++j) {
|
||||||
|
if (!j || !((j + 1) % 1000))
|
||||||
|
console.log("run #" + (j + 1));
|
||||||
|
ptr;
|
||||||
var ptrs = [];
|
var ptrs = [];
|
||||||
for (var i = 0; i < 256; ++i) {
|
// allocate some blocks of unusual sizes
|
||||||
var size = i * 64;
|
for (var i = 0; i < 2048; ++i) {
|
||||||
|
var size = i * 61;
|
||||||
ptr = tlsf.allocate_memory(size);
|
ptr = tlsf.allocate_memory(size);
|
||||||
console.log("allocate_memory(" + size + ") = " + ptr);
|
// immediately free every 4th
|
||||||
if (!(i % 4)) {
|
if (!(i % 4)) {
|
||||||
tlsf.free_memory(ptr);
|
tlsf.free_memory(ptr);
|
||||||
console.log("free_memory(" + ptr + ")");
|
} else {
|
||||||
} else
|
|
||||||
ptrs.push(ptr);
|
ptrs.push(ptr);
|
||||||
}
|
// randomly free random blocks (if not the first run that determines max memory)
|
||||||
while (ptrs.length) {
|
if (j && Math.random() < 0.25) {
|
||||||
ptr = Math.random() < 0.5 ? ptrs.pop() : ptrs.shift();
|
ptr = ptrs.splice((Math.random() * ptrs.length)|0, 1)[0];
|
||||||
console.log("free_memory(" + ptr + ")");
|
|
||||||
tlsf.free_memory(ptr);
|
tlsf.free_memory(ptr);
|
||||||
}
|
}
|
||||||
if (memSize && memSize != tlsf.memory.length)
|
|
||||||
throw new Error("memory should not grow multiple times");
|
|
||||||
memSize = tlsf.memory.length;
|
|
||||||
}
|
}
|
||||||
var ptr = tlsf.allocate_memory(64);
|
}
|
||||||
console.log("allocate_memory(" + 64 + ") = " + ptr);
|
tlsf.check();
|
||||||
} catch (e) {
|
// clean up by randomly freeing all blocks
|
||||||
console.log(e.stack);
|
while (ptrs.length) {
|
||||||
mem(tlsf.memory);
|
ptr = ptrs.splice((Math.random() * ptrs.length)|0, 1)[0];
|
||||||
|
tlsf.free_memory(ptr);
|
||||||
|
}
|
||||||
|
if (memSize && memSize != tlsf.memory.buffer.byteLength)
|
||||||
|
throw new Error("memory should not grow multiple times: " + memSize + " != " + tlsf.memory.buffer.byteLength);
|
||||||
|
memSize = tlsf.memory.buffer.byteLength;
|
||||||
|
tlsf.check();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mem(tlsf.memory, 0, 4096);
|
||||||
|
console.log("memSize=" + memSize);
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
function mem(memory, offset) {
|
function mem(memory, offset, count) {
|
||||||
if (!offset) offset = 0;
|
if (!offset) offset = 0;
|
||||||
|
if (!count) count = 1024;
|
||||||
var mem = new Uint8Array(memory.buffer, offset);
|
var mem = new Uint8Array(memory.buffer, offset);
|
||||||
var stackTop = new Uint32Array(memory.buffer, 4, 1)[0];
|
var stackTop = new Uint32Array(memory.buffer, 4, 1)[0];
|
||||||
var hex = [];
|
var hex = [];
|
||||||
for (var i = 0; i < 1024; ++i) {
|
for (var i = 0; i < count; ++i) {
|
||||||
var o = (offset + i).toString(16);
|
var o = (offset + i).toString(16);
|
||||||
while (o.length < 3) o = "0" + o;
|
while (o.length < 3) o = "0" + o;
|
||||||
if ((i & 15) === 0) {
|
if ((i & 15) === 0) {
|
||||||
@ -55,3 +69,6 @@ function mem(memory, offset) {
|
|||||||
}
|
}
|
||||||
console.log(hex.join(" ") + " ...");
|
console.log(hex.join(" ") + " ...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("tlsf.untouched.wasm");
|
||||||
|
test("tlsf.optimized.wasm");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user