diff --git a/src/compiler.ts b/src/compiler.ts index 5520d0eb..7d2b0f59 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -8378,6 +8378,66 @@ export class Compiler extends DiagnosticEmitter { return stmts; } + /** Wraps a reference in a `retain` call. Returns the reference if `tempLocal` is specified. */ + makeRetain(valueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef { + var module = this.module; + var program = this.program; + var retainFn = assert(program.retainRef); + this.compileFunction(retainFn); + if (tempIndex >= 0) { + let nativeSizeType = this.options.nativeSizeType; + return module.createBlock(null, [ + module.createCall(retainFn.internalName, [ + module.createTeeLocal(tempIndex, valueExpr) + ], NativeType.None), + module.createGetLocal(tempIndex, nativeSizeType) + ], nativeSizeType); + } else { + return module.createCall(retainFn.internalName, [ valueExpr ], NativeType.None); + } + } + + /** Wraps a reference in `release` call. Returns the reference if `tempLocal` is specified. */ + makeRelease(valueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef { + var module = this.module; + var program = this.program; + var releaseFn = assert(program.releaseRef); + this.compileFunction(releaseFn); + if (tempIndex >= 0) { + let nativeSizeType = this.options.nativeSizeType; + return module.createBlock(null, [ + module.createCall(releaseFn.internalName, [ + module.createTeeLocal(tempIndex, valueExpr) + ], NativeType.None), + module.createGetLocal(tempIndex, nativeSizeType) + ], nativeSizeType); + } else { + return module.createCall(releaseFn.internalName, [ valueExpr ], NativeType.None); + } + } + + /** Wraps a new and an old reference in a sequence of `retain` and `release` calls. */ + makeRetainRelease(newValueExpr: ExpressionRef, oldValueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef { + // TODO: checking `newValue != oldValue` significantly reduces strain on the roots buffer + // when cyclic structures may be immediately released but also requires a tempIndex. might + // be worth to require a temp here. furthermore it might be worth to require it for retain + // and release as well so we can emit != null checks where necessary only? + var module = this.module; + if (tempIndex >= 0) { + let nativeSizeType = this.options.nativeSizeType; + return module.createBlock(null, [ + this.makeRetain(module.createTeeLocal(tempIndex, newValueExpr)), + this.makeRelease(oldValueExpr), + module.createGetLocal(tempIndex, nativeSizeType) + ], nativeSizeType); + } else { + return module.createBlock(null, [ + this.makeRetain(newValueExpr), + this.makeRelease(oldValueExpr) + ]); + } + } + /** Prepares the insertion of a reference into an _uninitialized_ parent using the GC interface. */ makeInsertRef( valueExpr: ExpressionRef, diff --git a/std/assembly/rt/README.md b/std/assembly/rt/README.md new file mode 100644 index 00000000..c930576e --- /dev/null +++ b/std/assembly/rt/README.md @@ -0,0 +1,6 @@ +The AssemblyScript Runtime +========================== + +The runtime provides the functionality necessary to dynamically allocate and deallocate memory of objects, arrays and buffers, as well as keep track of references that are no longer used. + +It is based on [the TLSF memory manager](./tlsf.ts) and [a pure reference counting garbage collector](./pure.ts). diff --git a/std/assembly/rt/common.ts b/std/assembly/rt/common.ts new file mode 100644 index 00000000..e8062635 --- /dev/null +++ b/std/assembly/rt/common.ts @@ -0,0 +1,13 @@ +// Alignment guarantees + +// @ts-ignore: decorator +@inline export const AL_BITS: u32 = 4; // 16 bytes to fit up to v128 +// @ts-ignore: decorator +@inline export const AL_SIZE: usize = 1 << AL_BITS; +// @ts-ignore: decorator +@inline export const AL_MASK: usize = AL_SIZE - 1; + +// Extra debugging + +// @ts-ignore: decorator +@inline export const DEBUG = true; diff --git a/std/assembly/rt/index.ts b/std/assembly/rt/index.ts new file mode 100644 index 00000000..cdd95244 --- /dev/null +++ b/std/assembly/rt/index.ts @@ -0,0 +1,52 @@ +import { AL_MASK, DEBUG } from "./common"; +import { ROOT, Block, BLOCK_OVERHEAD, initializeRoot, allocateBlock, reallocateBlock, freeBlock } from "./tlsf"; +import { increment, decrement, collectCycles } from "./pure"; + +//////////////////////////////////// Memory manager interface ///////////////////////////////////// + +// @ts-ignore: decorator +@global @unsafe +function __mm_allocate(size: usize): usize { + var root = ROOT; + if (!root) { + initializeRoot(); + root = ROOT; + } + return changetype(allocateBlock(root, size)) + BLOCK_OVERHEAD; +} + +// @ts-ignore: decorator +@global @unsafe +function __mm_reallocate(data: usize, size: usize): usize { + if (DEBUG) assert(ROOT); // must be initialized + assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned + return changetype(reallocateBlock(ROOT, changetype(data - BLOCK_OVERHEAD), size)) + BLOCK_OVERHEAD; +} + +// @ts-ignore: decorator +@global @unsafe +function __mm_free(data: usize): void { + if (DEBUG) assert(ROOT); // must be initialized + assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned + freeBlock(ROOT, changetype(data - BLOCK_OVERHEAD)); +} + +/////////////////////////////////// Garbage collector interface /////////////////////////////////// + +// @ts-ignore: decorator +@global @unsafe +function __gc_retain(ref: usize): void { + if (ref) increment(changetype(ref - BLOCK_OVERHEAD)); +} + +// @ts-ignore: decorator +@global @unsafe +function __gc_release(ref: usize): void { + if (ref) decrement(changetype(ref - BLOCK_OVERHEAD)); +} + +// @ts-ignore: decorator +@global @unsafe +function __gc_collect(): void { + collectCycles(); +} diff --git a/std/assembly/rt/pure.ts b/std/assembly/rt/pure.ts new file mode 100644 index 00000000..80c90dcf --- /dev/null +++ b/std/assembly/rt/pure.ts @@ -0,0 +1,239 @@ +import { DEBUG } from "./common"; +import { Block, freeBlock, ROOT } from "./tlsf"; + +/////////////////////////// A Pure Reference Counting Garbage Collector /////////////////////////// +// see: https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon03Pure.pdf + +// TODO: make visitors eat cookies so we can compile direct calls into a switch +function __rt_visit_members(s: Block, cookie: i32): void { unreachable(); } +function __rt_flags(classId: u32): u32 { return unreachable(); } +const ACYCLIC_FLAG: u32 = 0; + +// ╒══════════════════════ GC Info structure ══════════════════════╕ +// │ 3 2 1 │ +// │1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0│ +// ├─┼─┴─┴─┼─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ +// │B│color│ refCount │ +// └─┴─────┴───────────────────────────────────────────────────────┘ +// B: buffered + +// @ts-ignore: decorator +@inline const BUFFERED_MASK: u32 = 1 << (sizeof() * 8 - 1); +// @ts-ignore: decorator +@inline const COLOR_BITS = 3; +// @ts-ignore: decorator +@inline const COLOR_SHIFT: u32 = ctz(BUFFERED_MASK) - COLOR_BITS; +// @ts-ignore: decorator +@inline const COLOR_MASK: u32 = ((1 << COLOR_BITS) - 1) << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const REFCOUNT_MASK: u32 = (1 << COLOR_SHIFT) - 1; + +// ╒════════╤═══════════════════ Colors ═══════════════════════════╕ +// │ Color │ Meaning │ +// ├────────┼──────────────────────────────────────────────────────┤ +// │ BLACK │ In use or free │ +// │ GRAY │ Possible member of cycle │ +// │ WHITE │ Member of garbage cycle │ +// │ PURPLE │ Possible root of cycle │ +// │ RED │ Candidate cycle undergoing Σ-computation *concurrent │ +// │ ORANGE │ Candidate cycle awaiting epoch boundary *concurrent │ +// └────────┴──────────────────────────────────────────────────────┘ +// Acyclic detection has been decoupled, hence no GREEN. + +// @ts-ignore: decorator +@inline const COLOR_BLACK: u32 = 0 << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const COLOR_GRAY: u32 = 1 << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const COLOR_WHITE: u32 = 2 << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const COLOR_PURPLE: u32 = 3 << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const COLOR_RED: u32 = 4 << COLOR_SHIFT; +// @ts-ignore: decorator +@inline const COLOR_ORANGE: u32 = 5 << COLOR_SHIFT; + +// @ts-ignore: decorator +@inline const VISIT_DECREMENT = 1; // guard 0 +// @ts-ignore: decorator +@inline const VISIT_MARKGRAY = 2; +// @ts-ignore: decorator +@inline const VISIT_SCAN = 3; +// @ts-ignore: decorator +@inline const VISIT_SCANBLACK = 4; +// @ts-ignore: decorator +@inline const VISIT_COLLECTWHITE = 5; + +// @ts-ignore: decorator +@global +function __rt_visit(s: Block, cookie: i32): void { + switch (cookie) { + case VISIT_DECREMENT: { + decrement(s); + break; + } + case VISIT_MARKGRAY: { + if (DEBUG) assert((s.gcInfo & REFCOUNT_MASK) > 0); + s.gcInfo = s.gcInfo - 1; + markGray(s); + break; + } + case VISIT_SCAN: { + scan(s); + break; + } + case VISIT_SCANBLACK: { + let info = s.gcInfo; + assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow + s.gcInfo = info + 1; + if ((info & COLOR_MASK) != COLOR_BLACK) { + scanBlack(s); + } + break; + } + case VISIT_COLLECTWHITE: { + collectWhite(s); + break; + } + default: if (DEBUG) assert(false); + } +} + +/** Increments the reference count of the specified block by one.*/ +export function increment(s: Block): void { + var info = s.gcInfo; + assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow + s.gcInfo = info + 1; +} + +/** Decrements the reference count of the specified block by one, possibly freeing it. */ +export function decrement(s: Block): void { + var info = s.gcInfo; + var rc = info & REFCOUNT_MASK; + if (rc == 1) { + __rt_visit_members(s, VISIT_DECREMENT); + if (!(info & BUFFERED_MASK)) { + freeBlock(ROOT, s); + } else { + s.gcInfo = BUFFERED_MASK | COLOR_BLACK | 0; + } + } else { + if (DEBUG) assert(rc > 0); + if (!(__rt_flags(s.rtId) & ACYCLIC_FLAG)) { + s.gcInfo = BUFFERED_MASK | COLOR_PURPLE | (rc - 1); + if (!(info & BUFFERED_MASK)) { + appendRoot(s); + } + } else { + s.gcInfo = (info & ~REFCOUNT_MASK) | (rc - 1); + } + } +} + +/** Buffer of possible roots. */ +// @ts-ignore: decorator +@lazy var ROOTS: usize; +/** Current absolute offset into the `ROOTS` buffer. */ +// @ts-ignore: decorator +@lazy var CUR: usize = 0; +/** Current absolute end offset into the `ROOTS` buffer. */ +// @ts-ignore: decorator +@lazy var END: usize = 0; + +/** Appends a block to possible roots. */ +function appendRoot(s: Block): void { + var cur = CUR; + if (cur >= END) { + growRoots(); // TBD: either that or pick a default and force collection on overflow + cur = CUR; + } + store(cur, s); + CUR = cur + 1; +} + +/** Grows the roots buffer if it ran full. */ +function growRoots(): void { + var oldRoots = ROOTS; + var oldSize = CUR - oldRoots; + var newSize = max(oldSize * 2, 64 << alignof()); + var newRoots = memory.allocate(newSize); + memory.copy(newRoots, oldRoots, oldSize); + ROOTS = newRoots; + CUR = newRoots + oldSize; + END = newRoots + newSize; +} + +/** Collects cyclic garbage. */ +export function collectCycles(): void { + + // markRoots + var roots = ROOTS; + var cur = roots; + for (let pos = cur, end = CUR; pos < end; pos += sizeof()) { + let s = load(pos); + let info = s.gcInfo; + if ((info & COLOR_MASK) == COLOR_PURPLE && (info & REFCOUNT_MASK) > 0) { + markGray(s); + store(cur, s); + cur += sizeof(); + } else { + if ((info & COLOR_MASK) == COLOR_BLACK && !(info & REFCOUNT_MASK)) { + freeBlock(ROOT, s); + } else { + s.gcInfo = info & ~BUFFERED_MASK; + } + } + } + CUR = cur; + + // scanRoots + for (let pos = roots; pos < cur; pos += sizeof()) { + scan(load(pos)); + } + + // collectRoots + for (let pos = roots; pos < cur; pos += sizeof()) { + let s = load(pos); + s.gcInfo = s.gcInfo & ~BUFFERED_MASK; + collectWhite(s); + } + CUR = roots; +} + +/** Marks a block as gray (possible member of cycle) during the collection phase. */ +function markGray(s: Block): void { + var info = s.gcInfo; + if ((info & COLOR_MASK) != COLOR_GRAY) { + s.gcInfo = (info & ~COLOR_MASK) | COLOR_GRAY; + __rt_visit_members(s, VISIT_MARKGRAY); + } +} + +/** Scans a block during the collection phase, determining whether it is garbage or not. */ +function scan(s: Block): void { + var info = s.gcInfo; + if ((info & COLOR_MASK) == COLOR_GRAY) { + if ((info & REFCOUNT_MASK) > 0) { + scanBlack(s); + } else { + s.gcInfo = (info & ~COLOR_MASK) | COLOR_WHITE; + __rt_visit_members(s, VISIT_SCAN); + } + } +} + +/** Marks a block as black (in use) if it was found to be reachable during the collection phase. */ +function scanBlack(s: Block): void { + s.gcInfo = (s.gcInfo & ~COLOR_MASK) | COLOR_BLACK; + __rt_visit_members(s, VISIT_SCANBLACK); +} + +/** Collects all white (member of a garbage cycle) nodes when completing the collection phase. */ +function collectWhite(s: Block): void { + var info = s.gcInfo; + if ((info & COLOR_MASK) == COLOR_WHITE && !(info & BUFFERED_MASK)) { + // s.gcInfo = (info & ~COLOR_MASK) | COLOR_BLACK; + __rt_visit_members(s, VISIT_COLLECTWHITE); + } + freeBlock(ROOT, s); +} diff --git a/std/assembly/rt/tlsf.ts b/std/assembly/rt/tlsf.ts new file mode 100644 index 00000000..8b9bfbea --- /dev/null +++ b/std/assembly/rt/tlsf.ts @@ -0,0 +1,522 @@ +import { AL_BITS, AL_SIZE, AL_MASK, DEBUG } from "./common"; + +/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator /////////////////////// +// see: http://www.gii.upv.es/tlsf/ + +// - `ffs(x)` is equivalent to `ctz(x)` with x != 0 +// - `fls(x)` is equivalent to `sizeof(x) * 8 - clz(x) - 1` + +// ╒══════════════ Block size interpretation (32-bit) ═════════════╕ +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─╫─┴─┴─┴─┤ +// │ | FL │ SB = SL + AL │ ◄─ usize +// └───────────────────────────────────────────────┴───────╨───────┘ +// FL: first level, SL: second level, AL: alignment, SB: small block + +// @ts-ignore: decorator +@inline const SL_BITS: u32 = 4; +// @ts-ignore: decorator +@inline const SL_SIZE: usize = 1 << SL_BITS; + +// @ts-ignore: decorator +@inline const SB_BITS: usize = (SL_BITS + AL_BITS); +// @ts-ignore: decorator +@inline const SB_SIZE: usize = 1 << SB_BITS; + +// @ts-ignore: decorator +@inline const FL_BITS: u32 = 31 - SB_BITS; + +// [00]: < 256B (SB) [12]: < 1M +// [01]: < 512B [13]: < 2M +// [02]: < 1K [14]: < 4M +// [03]: < 2K [15]: < 8M +// [04]: < 4K [16]: < 16M +// [05]: < 8K [17]: < 32M +// [06]: < 16K [18]: < 64M +// [07]: < 32K [19]: < 128M +// [08]: < 64K [20]: < 256M +// [09]: < 128K [21]: < 512M +// [10]: < 256K [22]: <= 1G - OVERHEAD +// [11]: < 512K +// VMs limit to 2GB total (currently), making one 1G block max (or three 512M etc.) due to block overhead + +// Tags stored in otherwise unused alignment bits + +// @ts-ignore: decorator +@inline const FREE: usize = 1 << 0; +// @ts-ignore: decorator +@inline const LEFTFREE: usize = 1 << 1; +// @ts-ignore: decorator +@inline const TAGS_MASK: usize = FREE | LEFTFREE; // <= AL_MASK + +// ╒════════════════════ Block layout (32-bit) ════════════════════╕ +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┼─┤ overhead ┐ +// │ size │0│L│F│ ◄─┐ info +// ├─────────────────────────────────────────────────────────┴─┴─┴─┤ │ +// │ │ │ +// │ ... additional runtime overhead ... │ │ +// │ │ │ +// ╞═══════════════════════════════════════════════════════════════╡ │ ┐ ┘ +// │ if free: ◄ prev │ ◄─┤ usize +// ├───────────────────────────────────────────────────────────────┤ │ +// │ if free: next ► │ ◄─┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ ... │ │ = 0 +// ├───────────────────────────────────────────────────────────────┤ │ +// │ if free: back ▲ │ ◄─┘ +// └───────────────────────────────────────────────────────────────┘ payload ┘ >= MIN SIZE +// F: FREE, L: LEFTFREE +@unmanaged export class Block { + + /** Memory manager info. */ + mmInfo: usize; // WASM64 might need adaption + /** Garbage collector info. */ + gcInfo: u32; + /** Runtime class id. */ + rtId: u32; + /** Runtime object size. */ + rtSize: u32; + + /** Previous free block, if any. Only valid if free, otherwise part of payload. */ + prev: Block | null; + /** Next free block, if any. Only valid if free, otherwise part of payload. */ + next: Block | null; + + // If the block is free, there is a 'back'reference at its end pointing at its start. +} + +// Block constants. Overhead is always present, no matter if free or used. Also, a block must have +// a minimum size of three pointers so it can hold `prev`, `next` and `back` if free. + +// @ts-ignore: decorator +@inline export const BLOCK_OVERHEAD: usize = (offsetof("prev") + AL_MASK) & ~AL_MASK; +// @ts-ignore: decorator +@inline const BLOCK_MINSIZE: usize = (3 * sizeof() + AL_MASK) & ~AL_MASK;// prev + next + back +// @ts-ignore: decorator +@inline const BLOCK_MAXSIZE: usize = 1 << (FL_BITS + SB_BITS - 1); // exclusive + +/** Gets the left block of a block. Only valid if the left block is free. */ +// @ts-ignore: decorator +@inline function GETFREELEFT(block: Block): Block { + return load(changetype(block) - sizeof()); +} + +/** Gets the right block of of a block by advancing to the right by its size. */ +// @ts-ignore: decorator +@inline function GETRIGHT(block: Block): Block { + return changetype(changetype(block) + BLOCK_OVERHEAD + (block.mmInfo & ~TAGS_MASK)); +} + +// ╒═════════════════════ Root layout (32-bit) ════════════════════╕ +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐ +// │ 0 | flMap S│ ◄────┐ +// ╞═══════════════════════════════════════════════════════════════╡ │ +// │ slMap[0] S │ ◄─┐ │ +// ├───────────────────────────────────────────────────────────────┤ │ │ +// │ slMap[1] │ ◄─┤ │ +// ├───────────────────────────────────────────────────────────────┤ u32 │ +// │ slMap[22] │ ◄─┘ │ +// ╞═══════════════════════════════════════════════════════════════╡ usize +// │ head[0] │ ◄────┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ ... │ ◄────┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ head[367] │ ◄────┤ +// ╞═══════════════════════════════════════════════════════════════╡ │ +// │ tail │ ◄────┘ +// └───────────────────────────────────────────────────────────────┘ SIZE ┘ +// S: Small blocks map +@unmanaged class Root { + /** First level bitmap. */ + flMap: usize; +} + +// Root constants. Where stuff is stored inside of the root structure. + +// @ts-ignore: decorator +@inline const SL_START = sizeof(); +// @ts-ignore: decorator +@inline const SL_END = SL_START + (FL_BITS << alignof()); +// @ts-ignore: decorator +@inline const HL_START = (SL_END + AL_MASK) & ~AL_MASK; +// @ts-ignore: decorator +@inline const HL_END = HL_START + FL_BITS * SL_SIZE * sizeof(); +// @ts-ignore: decorator +@inline const ROOT_SIZE = HL_END + sizeof(); + +// @ts-ignore: decorator +@lazy export var ROOT: Root; + +/** Gets the second level map of the specified first level. */ +// @ts-ignore: decorator +@inline function GETSL(root: Root, fl: usize): u32 { + return load(changetype(root) + (fl << alignof()), SL_START); +} + +/** Sets the second level map of the specified first level. */ +// @ts-ignore: decorator +@inline function SETSL(root: Root, fl: usize, slMap: u32): void { + store(changetype(root) + (fl << alignof()), slMap, SL_START); +} + +/** Gets the head of the free list for the specified combination of first and second level. */ +// @ts-ignore: decorator +@inline function GETHEAD(root: Root, fl: usize, sl: u32): Block | null { + return changetype(load(changetype(root) + (fl * SL_SIZE + sl) * sizeof(), HL_START)); +} + +/** Sets the head of the free list for the specified combination of first and second level. */ +// @ts-ignore: decorator +@inline function SETHEAD(root: Root, fl: usize, sl: u32, head: Block | null): void { + store(changetype(root) + (fl * SL_SIZE + sl) * sizeof() , changetype(head), HL_START); +} + +/** Gets the tail block.. */ +// @ts-ignore: decorator +@inline function GETTAIL(root: Root): Block { + return load(changetype(root), HL_END); +} + +/** Sets the tail block. */ +// @ts-ignore: decorator +@inline function SETTAIL(root: Root, tail: Block): void { + store(changetype(root), tail, HL_END); +} + +/** Inserts a previously used block back into the free list. */ +function insertBlock(root: Root, block: Block): void { + if (DEBUG) assert(block); // cannot be null + var blockInfo = block.mmInfo; + if (DEBUG) assert(blockInfo & FREE); // must be free + + var right = GETRIGHT(block); + var rightInfo = right.mmInfo; + + // merge with right block if also free + if (rightInfo & FREE) { + let newSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK); + if (newSize < BLOCK_MAXSIZE) { + removeBlock(root, right); + block.mmInfo = blockInfo = (blockInfo & TAGS_MASK) | newSize; + right = GETRIGHT(block); + rightInfo = right.mmInfo; + // 'back' is set below + } + } + + // merge with left block if also free + if (blockInfo & LEFTFREE) { + let left = GETFREELEFT(block); + let leftInfo = left.mmInfo; + if (DEBUG) assert(leftInfo & FREE); // must be free according to right tags + let newSize = (leftInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (blockInfo & ~TAGS_MASK); + if (newSize < BLOCK_MAXSIZE) { + removeBlock(root, left); + left.mmInfo = blockInfo = (leftInfo & TAGS_MASK) | newSize; + block = left; + // 'back' is set below + } + } + + right.mmInfo = rightInfo | LEFTFREE; + // right is no longer used now, hence rightInfo is not synced + + // we now know the size of the block + var size = blockInfo & ~TAGS_MASK; + if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be a valid size + if (DEBUG) assert(changetype(block) + BLOCK_OVERHEAD + size == changetype(right)); // must match + + // set 'back' to itself at the end of block + store(changetype(right) - sizeof(), block); + + // mapping_insert + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + const inv: usize = sizeof() * 8 - 1; + fl = inv - clz(size); + sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + } + if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range + + // perform insertion + var head = GETHEAD(root, fl, sl); + block.prev = null; + block.next = head; + if (head) head.prev = block; + SETHEAD(root, fl, sl, block); + + // update first and second level maps + root.flMap |= (1 << fl); + SETSL(root, fl, GETSL(root, fl) | (1 << sl)); +} + +/** Removes a free block from internal lists. */ +function removeBlock(root: Root, block: Block): void { + var blockInfo = block.mmInfo; + if (DEBUG) assert(blockInfo & FREE); // must be free + var size = blockInfo & ~TAGS_MASK; + if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be valid + + // mapping_insert + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + const inv: usize = sizeof() * 8 - 1; + fl = inv - clz(size); + sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + } + if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range + + // link previous and next free block + var prev = block.prev; + var next = block.next; + if (prev) prev.next = next; + if (next) next.prev = prev; + + // update head if we are removing it + if (block == GETHEAD(root, fl, sl)) { + SETHEAD(root, fl, sl, next); + + // clear second level map if head is empty now + if (!next) { + let slMap = GETSL(root, fl); + SETSL(root, fl, slMap &= ~(1 << sl)); + + // clear first level map if second level is empty now + if (!slMap) root.flMap &= ~(1 << fl); + } + } + // note: does not alter left/back because it is likely that splitting + // is performed afterwards, invalidating those changes. so, the caller + // must perform those updates. +} + +/** Searches for a free block of at least the specified size. */ +function searchBlock(root: Root, size: usize): Block | null { + // size was already asserted by caller + + // mapping_search + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + const halfMaxSize = BLOCK_MAXSIZE >> 1; // don't round last fl + const inv: usize = sizeof() * 8 - 1; + const invRound = inv - SL_BITS; + let requestSize = size < halfMaxSize + ? size + (1 << (invRound - clz(size))) - 1 + : size; + fl = inv - clz(requestSize); + sl = ((requestSize >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + } + if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range + + // search second level + var slMap = GETSL(root, fl) & (~0 << sl); + var head: Block | null; + if (!slMap) { + // search next larger first level + let flMap = root.flMap & (~0 << (fl + 1)); + if (!flMap) { + head = null; + } else { + fl = ctz(flMap); + slMap = GETSL(root, fl); + if (DEBUG) assert(slMap); // can't be zero if fl points here + head = GETHEAD(root, fl, ctz(slMap)); + } + } else { + head = GETHEAD(root, fl, ctz(slMap)); + } + return head; +} + +/** Prepares the specified block before (re-)use, possibly splitting it. */ +function prepareBlock(root: Root, block: Block, size: usize): void { + // size was already asserted by caller + + var blockInfo = block.mmInfo; + if (DEBUG) assert(!(size & AL_MASK)); // size must be aligned so the new block is + + // split if the block can hold another MINSIZE block incl. overhead + var remaining = (blockInfo & ~TAGS_MASK) - size; + if (remaining >= BLOCK_OVERHEAD + BLOCK_MINSIZE) { + block.mmInfo = size | (blockInfo & LEFTFREE); // also discards FREE + + let spare = changetype(changetype(block) + BLOCK_OVERHEAD + size); + spare.mmInfo = (remaining - BLOCK_OVERHEAD) | FREE; // not LEFTFREE + insertBlock(root, spare); // also sets 'back' + + // otherwise tag block as no longer FREE and right as no longer LEFTFREE + } else { + block.mmInfo = blockInfo & ~FREE; + GETRIGHT(block).mmInfo &= ~LEFTFREE; + } +} + +/** Adds more memory to the pool. */ +function addMemory(root: Root, start: usize, end: usize): bool { + if (DEBUG) { + assert( + start <= end && // must be valid + !(start & AL_MASK) && // must be aligned + !(end & AL_MASK) // must be aligned + ); + } + + var tail = GETTAIL(root); + var tailInfo: usize = 0; + if (tail) { // more memory + if (DEBUG) assert(start >= changetype(tail) + BLOCK_OVERHEAD); + + // merge with current tail if adjacent + if (start - BLOCK_OVERHEAD == changetype(tail)) { + start -= BLOCK_OVERHEAD; + tailInfo = tail.mmInfo; + } else { + // We don't do this, but a user might `memory.grow` manually + // leading to non-adjacent pages managed by TLSF. + } + + } else if (DEBUG) { // first memory + assert(start >= changetype(root) + ROOT_SIZE); // starts after root + } + + // check if size is large enough for a free block and the tail block + var size = end - start; + if (size < BLOCK_OVERHEAD + BLOCK_MINSIZE + BLOCK_OVERHEAD) { + return false; + } + + // left size is total minus its own and the zero-length tail's header + var leftSize = size - 2 * BLOCK_OVERHEAD; + var left = changetype(start); + left.mmInfo = leftSize | FREE | (tailInfo & LEFTFREE); + left.prev = null; + left.next = null; + + // tail is a zero-length used block + tail = changetype(start + size - BLOCK_OVERHEAD); + tail.mmInfo = 0 | LEFTFREE; + SETTAIL(root, tail); + + insertBlock(root, left); // also merges with free left before tail / sets 'back' + + return true; +} + +/** Grows memory to fit at least another block of the specified size. */ +function growMemory(root: Root, size: usize): void { + var pagesBefore = memory.size(); + var pagesNeeded = (((size + 0xffff) & ~0xffff) >>> 16); + var pagesWanted = max(pagesBefore, pagesNeeded); // double memory + if (memory.grow(pagesWanted) < 0) { + if (memory.grow(pagesNeeded) < 0) unreachable(); + } + var pagesAfter = memory.size(); + addMemory(root, pagesBefore << 16, pagesAfter << 16); +} + +/** Prepares and checks an allocation size. */ +function prepareSize(size: usize): usize { + if (size >= BLOCK_MAXSIZE) throw new Error("allocation too large"); + return max((size + AL_MASK) & ~AL_MASK, BLOCK_MINSIZE); // align and ensure min size +} + +/** Initilizes the root structure. */ +export function initializeRoot(): void { + var rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK; + var pagesBefore = memory.size(); + var pagesNeeded = ((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16); + if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable(); + var root = changetype(rootOffset); + root.flMap = 0; + SETTAIL(root, changetype(0)); + for (let fl: usize = 0; fl < FL_BITS; ++fl) { + SETSL(root, fl, 0); + for (let sl: u32 = 0; sl < SL_SIZE; ++sl) { + SETHEAD(root, fl, sl, null); + } + } + addMemory(root, (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16); + ROOT = root; +} + +/** Allocates a block of the specified size. */ +export function allocateBlock(root: Root, size: usize): Block { + var payloadSize = prepareSize(size); + var block = searchBlock(root, payloadSize); + if (!block) { + growMemory(root, payloadSize); + block = searchBlock(root, payloadSize); + if (DEBUG) assert(block); // must be found now + } + if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= payloadSize); // must fit + block.gcInfo = 0; + block.rtId = 0; // not determined yet + block.rtSize = size; + removeBlock(root, block); + prepareBlock(root, block, payloadSize); + return block; +} + +/** Reallocates a block to the specified size. */ +export function reallocateBlock(root: Root, block: Block, size: usize): Block { + var payloadSize = prepareSize(size); + var blockInfo = block.mmInfo; + if (DEBUG) assert(!(blockInfo & FREE)); // must be used + + // possibly split and update runtime size if it still fits + if (payloadSize <= (blockInfo & ~TAGS_MASK)) { + prepareBlock(root, block, payloadSize); + block.rtSize = size; + return block; + } + + // merge with right free block if merger is large enough + var right = GETRIGHT(block); + var rightInfo = right.mmInfo; + if (rightInfo & FREE) { + let mergeSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK); + if (mergeSize >= payloadSize) { + removeBlock(root, right); + // TODO: this can yield an intermediate block larger than BLOCK_MAXSIZE, which + // is immediately split though. does this trigger any assertions / issues? + block.mmInfo = (blockInfo & TAGS_MASK) | mergeSize; + block.rtSize = size; + prepareBlock(root, block, payloadSize); + return block; + } + } + + // otherwise move the block + var newBlock = allocateBlock(root, size); + newBlock.gcInfo = block.gcInfo; + newBlock.rtId = block.rtId; + memory.copy(changetype(newBlock) + BLOCK_OVERHEAD, changetype(block) + BLOCK_OVERHEAD, size); + block.mmInfo = blockInfo | FREE; + insertBlock(root, block); + return newBlock; +} + +/** Frees a block. */ +export function freeBlock(root: Root, block: Block): void { + var blockInfo = block.mmInfo; + assert(!(blockInfo & FREE)); // must be used (user might call through to this) + block.mmInfo = blockInfo | FREE; + insertBlock(root, block); +} diff --git a/tests/allocators/asrt/assembly/index.ts b/tests/allocators/asrt/assembly/index.ts index 0aa4ccc0..f2af0bf0 100644 --- a/tests/allocators/asrt/assembly/index.ts +++ b/tests/allocators/asrt/assembly/index.ts @@ -1,4 +1,4 @@ -import "../../../runtime/assembly/index"; +import "rt"; import { memory as builtin_memory } from "memory"; export namespace memory { diff --git a/tests/allocators/asrt/optimized.wat b/tests/allocators/asrt/optimized.wat index 315ac962..40afeeef 100644 --- a/tests/allocators/asrt/optimized.wat +++ b/tests/allocators/asrt/optimized.wat @@ -1,22 +1,21 @@ (module (type $FUNCSIG$ii (func (param i32) (result i32))) - (type $FUNCSIG$i (func (result i32))) + (type $FUNCSIG$v (func)) (type $FUNCSIG$vii (func (param i32 i32))) (type $FUNCSIG$iii (func (param i32 i32) (result i32))) (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$viii (func (param i32 i32 i32))) (type $FUNCSIG$vi (func (param i32))) - (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) - (data (i32.const 8) "\10\00\00\00>") - (data (i32.const 24) ".\00.\00/\00.\00.\00/\00r\00u\00n\00t\00i\00m\00e\00/\00a\00s\00s\00e\00m\00b\00l\00y\00/\00i\00n\00d\00e\00x\00.\00t\00s") - (global $../../runtime/assembly/index/ROOT (mut i32) (i32.const 0)) + (data (i32.const 8) "\10\00\00\00\1e") + (data (i32.const 24) "~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (export "memory" (memory $0)) (export "memory.allocate" (func $assembly/index/memory.allocate)) (export "memory.free" (func $assembly/index/memory.free)) (export "memory.fill" (func $assembly/index/memory.fill)) - (func $../../runtime/assembly/index/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -132,7 +131,7 @@ end end ) - (func $../../runtime/assembly/index/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -171,7 +170,7 @@ if local.get $0 local.get $4 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $2 i32.const 3 @@ -218,7 +217,7 @@ if local.get $0 local.get $3 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $3 local.get $6 i32.const 3 @@ -330,7 +329,7 @@ i32.or i32.store offset=4 ) - (func $../../runtime/assembly/index/addMemory (; 3 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/addMemory (; 3 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) local.get $2 block (result i32) @@ -392,14 +391,14 @@ i32.store offset=1568 local.get $0 local.get $1 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) - (func $../../runtime/assembly/index/initialize (; 4 ;) (type $FUNCSIG$i) (result i32) + (func $~lib/rt/tlsf/initializeRoot (; 4 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) - i32.const 96 + i32.const 64 local.tee $3 i32.const 67107 i32.add @@ -486,10 +485,11 @@ current_memory i32.const 16 i32.shl - call $../../runtime/assembly/index/addMemory + call $~lib/rt/tlsf/addMemory local.get $0 + global.set $~lib/rt/tlsf/ROOT ) - (func $../../runtime/assembly/index/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/tlsf/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) local.get $0 i32.const 1073741824 @@ -497,7 +497,7 @@ if i32.const 0 i32.const 24 - i32.const 466 + i32.const 436 i32.const 29 call $~lib/builtins/abort unreachable @@ -515,7 +515,7 @@ i32.gt_u select ) - (func $../../runtime/assembly/index/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) local.get $0 local.get $1 @@ -616,7 +616,7 @@ end end ) - (func $../../runtime/assembly/index/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -655,9 +655,9 @@ current_memory i32.const 16 i32.shl - call $../../runtime/assembly/index/addMemory + call $~lib/rt/tlsf/addMemory ) - (func $../../runtime/assembly/index/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) local.get $1 @@ -692,7 +692,7 @@ i32.store local.get $0 local.get $1 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock else local.get $1 local.get $3 @@ -721,23 +721,23 @@ i32.store end ) - (func $../../runtime/assembly/index/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) local.get $0 local.get $1 - call $../../runtime/assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.tee $3 - call $../../runtime/assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.tee $2 i32.eqz if local.get $0 local.get $3 - call $../../runtime/assembly/index/growMemory + call $~lib/rt/tlsf/growMemory local.get $0 local.get $3 - call $../../runtime/assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $2 end local.get $2 @@ -751,26 +751,25 @@ i32.store offset=12 local.get $0 local.get $2 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $0 local.get $2 local.get $3 - call $../../runtime/assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $2 ) (func $assembly/index/memory.allocate (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) - global.get $../../runtime/assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.tee $1 - i32.eqz - if - call $../../runtime/assembly/index/initialize - local.tee $1 - global.set $../../runtime/assembly/index/ROOT + if (result i32) + local.get $1 + else + call $~lib/rt/tlsf/initializeRoot + global.get $~lib/rt/tlsf/ROOT end - local.get $1 local.get $0 - call $../../runtime/assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock i32.const 16 i32.add ) @@ -784,9 +783,9 @@ i32.const 1 i32.or i32.store - global.get $../../runtime/assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) (func $~lib/memory/memory.fill (; 12 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i64) diff --git a/tests/allocators/asrt/untouched.wat b/tests/allocators/asrt/untouched.wat index 201974bd..5e0390d1 100644 --- a/tests/allocators/asrt/untouched.wat +++ b/tests/allocators/asrt/untouched.wat @@ -1,26 +1,26 @@ (module (type $FUNCSIG$ii (func (param i32) (result i32))) - (type $FUNCSIG$i (func (result i32))) + (type $FUNCSIG$v (func)) (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$vii (func (param i32 i32))) (type $FUNCSIG$iii (func (param i32 i32) (result i32))) (type $FUNCSIG$viii (func (param i32 i32 i32))) (type $FUNCSIG$vi (func (param i32))) - (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) - (data (i32.const 8) "\10\00\00\00>\00\00\00\00\00\00\00\00\00\00\00.\00.\00/\00.\00.\00/\00r\00u\00n\00t\00i\00m\00e\00/\00a\00s\00s\00e\00m\00b\00l\00y\00/\00i\00n\00d\00e\00x\00.\00t\00s\00") + (data (i32.const 8) "\10\00\00\00\1e\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00") + (data (i32.const 56) "\10\00\00\00 \00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00n\00d\00e\00x\00.\00t\00s\00") (table $0 1 funcref) (elem (i32.const 0) $null) - (global $../../runtime/assembly/index/ROOT (mut i32) (i32.const 0)) - (global $../../runtime/assembly/index/ACYCLIC_FLAG i32 (i32.const 0)) - (global $~lib/memory/HEAP_BASE i32 (i32.const 88)) + (global $~lib/rt/pure/ACYCLIC_FLAG i32 (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/memory/HEAP_BASE i32 (i32.const 104)) (export "memory" (memory $0)) (export "memory.allocate" (func $assembly/index/memory.allocate)) (export "memory.free" (func $assembly/index/memory.free)) (export "memory.fill" (func $assembly/index/memory.fill)) - (func $../../runtime/assembly/index/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -41,7 +41,7 @@ if i32.const 0 i32.const 24 - i32.const 276 + i32.const 265 i32.const 13 call $~lib/builtins/abort unreachable @@ -66,7 +66,7 @@ if i32.const 0 i32.const 24 - i32.const 278 + i32.const 267 i32.const 13 call $~lib/builtins/abort unreachable @@ -118,7 +118,7 @@ if i32.const 0 i32.const 24 - i32.const 291 + i32.const 280 i32.const 13 call $~lib/builtins/abort unreachable @@ -142,7 +142,7 @@ i32.store offset=16 end local.get $1 - block $../../runtime/assembly/index/GETHEAD|inlined.1 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.1 (result i32) local.get $0 local.set $10 local.get $4 @@ -162,7 +162,7 @@ end i32.eq if - block $../../runtime/assembly/index/SETHEAD|inlined.1 + block $~lib/rt/tlsf/SETHEAD|inlined.1 local.get $0 local.set $11 local.get $4 @@ -186,7 +186,7 @@ local.get $7 i32.eqz if - block $../../runtime/assembly/index/GETSL|inlined.0 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.0 (result i32) local.get $0 local.set $9 local.get $4 @@ -199,7 +199,7 @@ i32.load offset=4 end local.set $8 - block $../../runtime/assembly/index/SETSL|inlined.1 + block $~lib/rt/tlsf/SETSL|inlined.1 local.get $0 local.set $11 local.get $4 @@ -238,7 +238,7 @@ end end ) - (func $../../runtime/assembly/index/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -256,7 +256,7 @@ if i32.const 0 i32.const 24 - i32.const 204 + i32.const 193 i32.const 13 call $~lib/builtins/abort unreachable @@ -271,12 +271,12 @@ if i32.const 0 i32.const 24 - i32.const 206 + i32.const 195 i32.const 13 call $~lib/builtins/abort unreachable end - block $../../runtime/assembly/index/GETRIGHT|inlined.0 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.0 (result i32) local.get $1 local.set $3 local.get $3 @@ -318,7 +318,7 @@ if local.get $0 local.get $4 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $2 i32.const 3 @@ -327,7 +327,7 @@ i32.or local.tee $2 i32.store - block $../../runtime/assembly/index/GETRIGHT|inlined.1 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.1 (result i32) local.get $1 local.set $6 local.get $6 @@ -351,7 +351,7 @@ i32.const 2 i32.and if - block $../../runtime/assembly/index/GETFREELEFT|inlined.0 (result i32) + block $~lib/rt/tlsf/GETFREELEFT|inlined.0 (result i32) local.get $1 local.set $3 local.get $3 @@ -370,7 +370,7 @@ if i32.const 0 i32.const 24 - i32.const 227 + i32.const 216 i32.const 15 call $~lib/builtins/abort unreachable @@ -395,7 +395,7 @@ if local.get $0 local.get $3 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $3 local.get $6 i32.const 3 @@ -433,7 +433,7 @@ if i32.const 0 i32.const 24 - i32.const 242 + i32.const 231 i32.const 13 call $~lib/builtins/abort unreachable @@ -449,7 +449,7 @@ if i32.const 0 i32.const 24 - i32.const 243 + i32.const 232 i32.const 13 call $~lib/builtins/abort unreachable @@ -506,12 +506,12 @@ if i32.const 0 i32.const 24 - i32.const 259 + i32.const 248 i32.const 13 call $~lib/builtins/abort unreachable end - block $../../runtime/assembly/index/GETHEAD|inlined.2 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.2 (result i32) local.get $0 local.set $3 local.get $9 @@ -542,7 +542,7 @@ local.get $1 i32.store offset=16 end - block $../../runtime/assembly/index/SETHEAD|inlined.2 + block $~lib/rt/tlsf/SETHEAD|inlined.2 local.get $0 local.set $12 local.get $9 @@ -571,8 +571,8 @@ i32.shl i32.or i32.store - block $../../runtime/assembly/index/SETSL|inlined.2 - block $../../runtime/assembly/index/GETSL|inlined.1 (result i32) + block $~lib/rt/tlsf/SETSL|inlined.2 + block $~lib/rt/tlsf/GETSL|inlined.1 (result i32) local.get $0 local.set $13 local.get $9 @@ -598,7 +598,7 @@ i32.store offset=4 end ) - (func $../../runtime/assembly/index/addMemory (; 3 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $~lib/rt/tlsf/addMemory (; 3 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -629,12 +629,12 @@ if i32.const 0 i32.const 24 - i32.const 385 + i32.const 374 i32.const 4 call $~lib/builtins/abort unreachable end - block $../../runtime/assembly/index/GETTAIL|inlined.0 (result i32) + block $~lib/rt/tlsf/GETTAIL|inlined.0 (result i32) local.get $0 local.set $3 local.get $3 @@ -654,7 +654,7 @@ if i32.const 0 i32.const 24 - i32.const 395 + i32.const 384 i32.const 15 call $~lib/builtins/abort unreachable @@ -685,7 +685,7 @@ if i32.const 0 i32.const 24 - i32.const 407 + i32.const 396 i32.const 4 call $~lib/builtins/abort unreachable @@ -740,7 +740,7 @@ i32.const 2 i32.or i32.store - block $../../runtime/assembly/index/SETTAIL|inlined.1 + block $~lib/rt/tlsf/SETTAIL|inlined.1 local.get $0 local.set $9 local.get $4 @@ -751,10 +751,10 @@ end local.get $0 local.get $8 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock i32.const 1 ) - (func $../../runtime/assembly/index/initialize (; 4 ;) (type $FUNCSIG$i) (result i32) + (func $~lib/rt/tlsf/initializeRoot (; 4 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -808,7 +808,7 @@ local.get $3 i32.const 0 i32.store - block $../../runtime/assembly/index/SETTAIL|inlined.0 + block $~lib/rt/tlsf/SETTAIL|inlined.0 local.get $3 local.set $5 i32.const 0 @@ -827,7 +827,7 @@ i32.eqz br_if $break|0 block - block $../../runtime/assembly/index/SETSL|inlined.0 + block $~lib/rt/tlsf/SETSL|inlined.0 local.get $3 local.set $7 local.get $4 @@ -851,7 +851,7 @@ i32.lt_u i32.eqz br_if $break|1 - block $../../runtime/assembly/index/SETHEAD|inlined.0 + block $~lib/rt/tlsf/SETHEAD|inlined.0 local.get $3 local.set $9 local.get $4 @@ -904,11 +904,12 @@ current_memory i32.const 16 i32.shl - call $../../runtime/assembly/index/addMemory + call $~lib/rt/tlsf/addMemory drop local.get $3 + global.set $~lib/rt/tlsf/ROOT ) - (func $../../runtime/assembly/index/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/tlsf/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) (local $2 i32) local.get $0 @@ -917,7 +918,7 @@ if i32.const 0 i32.const 24 - i32.const 466 + i32.const 436 i32.const 29 call $~lib/builtins/abort unreachable @@ -937,7 +938,7 @@ i32.gt_u select ) - (func $../../runtime/assembly/index/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -1011,12 +1012,12 @@ if i32.const 0 i32.const 24 - i32.const 337 + i32.const 326 i32.const 13 call $~lib/builtins/abort unreachable end - block $../../runtime/assembly/index/GETSL|inlined.2 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.2 (result i32) local.get $0 local.set $5 local.get $2 @@ -1058,7 +1059,7 @@ local.get $4 i32.ctz local.set $2 - block $../../runtime/assembly/index/GETSL|inlined.3 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.3 (result i32) local.get $0 local.set $8 local.get $2 @@ -1076,12 +1077,12 @@ if i32.const 0 i32.const 24 - i32.const 350 + i32.const 339 i32.const 17 call $~lib/builtins/abort unreachable end - block $../../runtime/assembly/index/GETHEAD|inlined.3 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.3 (result i32) local.get $0 local.set $9 local.get $2 @@ -1103,7 +1104,7 @@ local.set $7 end else - block $../../runtime/assembly/index/GETHEAD|inlined.4 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.4 (result i32) local.get $0 local.set $8 local.get $2 @@ -1126,7 +1127,7 @@ end local.get $7 ) - (func $../../runtime/assembly/index/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -1176,10 +1177,10 @@ local.get $7 i32.const 16 i32.shl - call $../../runtime/assembly/index/addMemory + call $~lib/rt/tlsf/addMemory drop ) - (func $../../runtime/assembly/index/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -1194,7 +1195,7 @@ if i32.const 0 i32.const 24 - i32.const 364 + i32.const 353 i32.const 13 call $~lib/builtins/abort unreachable @@ -1235,7 +1236,7 @@ i32.store local.get $0 local.get $5 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock else local.get $1 local.get $3 @@ -1244,7 +1245,7 @@ i32.xor i32.and i32.store - block $../../runtime/assembly/index/GETRIGHT|inlined.3 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.3 (result i32) local.get $1 local.set $5 local.get $5 @@ -1258,7 +1259,7 @@ i32.and i32.add end - block $../../runtime/assembly/index/GETRIGHT|inlined.2 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.2 (result i32) local.get $1 local.set $5 local.get $5 @@ -1280,32 +1281,32 @@ i32.store end ) - (func $../../runtime/assembly/index/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) local.get $1 - call $../../runtime/assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.set $2 local.get $0 local.get $2 - call $../../runtime/assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $3 local.get $3 i32.eqz if local.get $0 local.get $2 - call $../../runtime/assembly/index/growMemory + call $~lib/rt/tlsf/growMemory local.get $0 local.get $2 - call $../../runtime/assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $3 local.get $3 i32.eqz if i32.const 0 i32.const 24 - i32.const 477 + i32.const 466 i32.const 15 call $~lib/builtins/abort unreachable @@ -1323,7 +1324,7 @@ if i32.const 0 i32.const 24 - i32.const 479 + i32.const 468 i32.const 13 call $~lib/builtins/abort unreachable @@ -1339,35 +1340,35 @@ i32.store offset=12 local.get $0 local.get $3 - call $../../runtime/assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $0 local.get $3 local.get $2 - call $../../runtime/assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $3 ) - (func $../../runtime/assembly/index/__mm_allocate (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/index/__mm_allocate (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) - global.get $../../runtime/assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.set $1 local.get $1 i32.eqz if - call $../../runtime/assembly/index/initialize - local.tee $1 - global.set $../../runtime/assembly/index/ROOT + call $~lib/rt/tlsf/initializeRoot + global.get $~lib/rt/tlsf/ROOT + local.set $1 end local.get $1 local.get $0 - call $../../runtime/assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock i32.const 16 i32.add ) (func $assembly/index/memory.allocate (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 - call $../../runtime/assembly/index/__mm_allocate + call $~lib/rt/index/__mm_allocate ) - (func $../../runtime/assembly/index/freeBlock (; 12 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/freeBlock (; 12 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) local.get $1 i32.load @@ -1380,7 +1381,7 @@ if i32.const 0 i32.const 24 - i32.const 530 + i32.const 519 i32.const 2 call $~lib/builtins/abort unreachable @@ -1392,15 +1393,15 @@ i32.store local.get $0 local.get $1 - call $../../runtime/assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) - (func $../../runtime/assembly/index/__mm_free (; 13 ;) (type $FUNCSIG$vi) (param $0 i32) - global.get $../../runtime/assembly/index/ROOT + (func $~lib/rt/index/__mm_free (; 13 ;) (type $FUNCSIG$vi) (param $0 i32) + global.get $~lib/rt/tlsf/ROOT i32.eqz if i32.const 0 - i32.const 24 - i32.const 556 + i32.const 72 + i32.const 29 i32.const 13 call $~lib/builtins/abort unreachable @@ -1419,21 +1420,21 @@ i32.eqz if i32.const 0 - i32.const 24 - i32.const 557 + i32.const 72 + i32.const 30 i32.const 2 call $~lib/builtins/abort unreachable end - global.get $../../runtime/assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 i32.const 16 i32.sub - call $../../runtime/assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock ) (func $assembly/index/memory.free (; 14 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 - call $../../runtime/assembly/index/__mm_free + call $~lib/rt/index/__mm_free ) (func $~lib/memory/memory.fill (; 15 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) diff --git a/tests/runtime/assembly/index.ts b/tests/runtime/assembly/index.ts index 3129d070..e10336b7 100644 --- a/tests/runtime/assembly/index.ts +++ b/tests/runtime/assembly/index.ts @@ -1,823 +1,12 @@ -// An experimental standalone AssemblyScript runtime based on TLSF and PureRC. +import "rt"; -// @ts-ignore: decorator -@inline const DEBUG = true; - -// Alignment guarantees - -// @ts-ignore: decorator -@inline const AL_BITS: u32 = 4; // 16 bytes to fit up to v128 -// @ts-ignore: decorator -@inline const AL_SIZE: usize = 1 << AL_BITS; -// @ts-ignore: decorator -@inline const AL_MASK: usize = AL_SIZE - 1; - -/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator /////////////////////// -// see: http://www.gii.upv.es/tlsf/ - -// - `ffs(x)` is equivalent to `ctz(x)` with x != 0 -// - `fls(x)` is equivalent to `sizeof(x) * 8 - clz(x) - 1` - -// ╒══════════════ Block size interpretation (32-bit) ═════════════╕ -// 3 2 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits -// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─╫─┴─┴─┴─┤ -// │ | FL │ SB = SL + AL │ ◄─ usize -// └───────────────────────────────────────────────┴───────╨───────┘ -// FL: first level, SL: second level, AL: alignment, SB: small block - -// @ts-ignore: decorator -@inline const SL_BITS: u32 = 4; -// @ts-ignore: decorator -@inline const SL_SIZE: usize = 1 << SL_BITS; - -// @ts-ignore: decorator -@inline const SB_BITS: usize = (SL_BITS + AL_BITS); -// @ts-ignore: decorator -@inline const SB_SIZE: usize = 1 << SB_BITS; - -// @ts-ignore: decorator -@inline const FL_BITS: u32 = 31 - SB_BITS; - -// [00]: < 256B (SB) [12]: < 1M -// [01]: < 512B [13]: < 2M -// [02]: < 1K [14]: < 4M -// [03]: < 2K [15]: < 8M -// [04]: < 4K [16]: < 16M -// [05]: < 8K [17]: < 32M -// [06]: < 16K [18]: < 64M -// [07]: < 32K [19]: < 128M -// [08]: < 64K [20]: < 256M -// [09]: < 128K [21]: < 512M -// [10]: < 256K [22]: <= 1G - OVERHEAD -// [11]: < 512K -// VMs limit to 2GB total (currently), making one 1G block max (or three 512M etc.) due to block overhead - -// Tags stored in otherwise unused alignment bits - -// @ts-ignore: decorator -@inline const FREE: usize = 1 << 0; -// @ts-ignore: decorator -@inline const LEFTFREE: usize = 1 << 1; -// @ts-ignore: decorator -@inline const TAGS_MASK: usize = FREE | LEFTFREE; // <= AL_MASK - -// ╒════════════════════ Block layout (32-bit) ════════════════════╕ -// 3 2 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits -// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┼─┤ overhead ┐ -// │ size │0│L│F│ ◄─┐ info -// ├─────────────────────────────────────────────────────────┴─┴─┴─┤ │ -// │ │ │ -// │ ... additional runtime overhead ... │ │ -// │ │ │ -// ╞═══════════════════════════════════════════════════════════════╡ │ ┐ ┘ -// │ if free: ◄ prev │ ◄─┤ usize -// ├───────────────────────────────────────────────────────────────┤ │ -// │ if free: next ► │ ◄─┤ -// ├───────────────────────────────────────────────────────────────┤ │ -// │ ... │ │ = 0 -// ├───────────────────────────────────────────────────────────────┤ │ -// │ if free: back ▲ │ ◄─┘ -// └───────────────────────────────────────────────────────────────┘ payload ┘ >= MIN SIZE -// F: FREE, L: LEFTFREE -@unmanaged class Block { - - /** Memory manager info. */ - mmInfo: usize; // WASM64 might need adaption - /** Garbage collector info. */ - gcInfo: u32; - /** Runtime class id. */ - rtId: u32; - /** Runtime object size. */ - rtSize: u32; - - /** Previous free block, if any. Only valid if free, otherwise part of payload. */ - prev: Block | null; - /** Next free block, if any. Only valid if free, otherwise part of payload. */ - next: Block | null; - - // If the block is free, there is a 'back'reference at its end pointing at its start. -} - -// Block constants. Overhead is always present, no matter if free or used. Also, a block must have -// a minimum size of three pointers so it can hold `prev`, `next` and `back` if free. - -// @ts-ignore: decorator -@inline const BLOCK_OVERHEAD: usize = (offsetof("prev") + AL_MASK) & ~AL_MASK; -// @ts-ignore: decorator -@inline const BLOCK_MINSIZE: usize = (3 * sizeof() + AL_MASK) & ~AL_MASK;// prev + next + back -// @ts-ignore: decorator -@inline const BLOCK_MAXSIZE: usize = 1 << (FL_BITS + SB_BITS - 1); // exclusive - -/** Gets the left block of a block. Only valid if the left block is free. */ -// @ts-ignore: decorator -@inline function GETFREELEFT(block: Block): Block { - return load(changetype(block) - sizeof()); -} - -/** Gets the right block of of a block by advancing to the right by its size. */ -// @ts-ignore: decorator -@inline function GETRIGHT(block: Block): Block { - return changetype(changetype(block) + BLOCK_OVERHEAD + (block.mmInfo & ~TAGS_MASK)); -} - -// ╒═════════════════════ Root layout (32-bit) ════════════════════╕ -// 3 2 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits -// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐ -// │ 0 | flMap S│ ◄────┐ -// ╞═══════════════════════════════════════════════════════════════╡ │ -// │ slMap[0] S │ ◄─┐ │ -// ├───────────────────────────────────────────────────────────────┤ │ │ -// │ slMap[1] │ ◄─┤ │ -// ├───────────────────────────────────────────────────────────────┤ u32 │ -// │ slMap[22] │ ◄─┘ │ -// ╞═══════════════════════════════════════════════════════════════╡ usize -// │ head[0] │ ◄────┤ -// ├───────────────────────────────────────────────────────────────┤ │ -// │ ... │ ◄────┤ -// ├───────────────────────────────────────────────────────────────┤ │ -// │ head[367] │ ◄────┤ -// ╞═══════════════════════════════════════════════════════════════╡ │ -// │ tail │ ◄────┘ -// └───────────────────────────────────────────────────────────────┘ SIZE ┘ -// S: Small blocks map -@unmanaged class Root { - /** First level bitmap. */ - flMap: usize; -} - -// Root constants. Where stuff is stored inside of the root structure. - -// @ts-ignore: decorator -@inline const SL_START = sizeof(); -// @ts-ignore: decorator -@inline const SL_END = SL_START + (FL_BITS << alignof()); -// @ts-ignore: decorator -@inline const HL_START = (SL_END + AL_MASK) & ~AL_MASK; -// @ts-ignore: decorator -@inline const HL_END = HL_START + FL_BITS * SL_SIZE * sizeof(); -// @ts-ignore: decorator -@inline const ROOT_SIZE = HL_END + sizeof(); - -var ROOT: Root; - -/** Gets the second level map of the specified first level. */ -// @ts-ignore: decorator -@inline function GETSL(root: Root, fl: usize): u32 { - return load(changetype(root) + (fl << alignof()), SL_START); -} - -/** Sets the second level map of the specified first level. */ -// @ts-ignore: decorator -@inline function SETSL(root: Root, fl: usize, slMap: u32): void { - store(changetype(root) + (fl << alignof()), slMap, SL_START); -} - -/** Gets the head of the free list for the specified combination of first and second level. */ -// @ts-ignore: decorator -@inline function GETHEAD(root: Root, fl: usize, sl: u32): Block | null { - return changetype(load(changetype(root) + (fl * SL_SIZE + sl) * sizeof(), HL_START)); -} - -/** Sets the head of the free list for the specified combination of first and second level. */ -// @ts-ignore: decorator -@inline function SETHEAD(root: Root, fl: usize, sl: u32, head: Block | null): void { - store(changetype(root) + (fl * SL_SIZE + sl) * sizeof() , changetype(head), HL_START); -} - -/** Gets the tail block.. */ -// @ts-ignore: decorator -@inline function GETTAIL(root: Root): Block { - return load(changetype(root), HL_END); -} - -/** Sets the tail block. */ -// @ts-ignore: decorator -@inline function SETTAIL(root: Root, tail: Block): void { - store(changetype(root), tail, HL_END); -} - -/** Inserts a previously used block back into the free list. */ -function insertBlock(root: Root, block: Block): void { - if (DEBUG) assert(block); // cannot be null - var blockInfo = block.mmInfo; - if (DEBUG) assert(blockInfo & FREE); // must be free - - var right = GETRIGHT(block); - var rightInfo = right.mmInfo; - - // merge with right block if also free - if (rightInfo & FREE) { - let newSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK); - if (newSize < BLOCK_MAXSIZE) { - removeBlock(root, right); - block.mmInfo = blockInfo = (blockInfo & TAGS_MASK) | newSize; - right = GETRIGHT(block); - rightInfo = right.mmInfo; - // 'back' is set below - } - } - - // merge with left block if also free - if (blockInfo & LEFTFREE) { - let left = GETFREELEFT(block); - let leftInfo = left.mmInfo; - if (DEBUG) assert(leftInfo & FREE); // must be free according to right tags - let newSize = (leftInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (blockInfo & ~TAGS_MASK); - if (newSize < BLOCK_MAXSIZE) { - removeBlock(root, left); - left.mmInfo = blockInfo = (leftInfo & TAGS_MASK) | newSize; - block = left; - // 'back' is set below - } - } - - right.mmInfo = rightInfo | LEFTFREE; - // right is no longer used now, hence rightInfo is not synced - - // we now know the size of the block - var size = blockInfo & ~TAGS_MASK; - if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be a valid size - if (DEBUG) assert(changetype(block) + BLOCK_OVERHEAD + size == changetype(right)); // must match - - // set 'back' to itself at the end of block - store(changetype(right) - sizeof(), block); - - // mapping_insert - var fl: usize, sl: u32; - if (size < SB_SIZE) { - fl = 0; - sl = (size / AL_SIZE); - } else { - const inv: usize = sizeof() * 8 - 1; - fl = inv - clz(size); - sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); - fl -= SB_BITS - 1; - } - if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range - - // perform insertion - var head = GETHEAD(root, fl, sl); - block.prev = null; - block.next = head; - if (head) head.prev = block; - SETHEAD(root, fl, sl, block); - - // update first and second level maps - root.flMap |= (1 << fl); - SETSL(root, fl, GETSL(root, fl) | (1 << sl)); -} - -/** Removes a free block from internal lists. */ -function removeBlock(root: Root, block: Block): void { - var blockInfo = block.mmInfo; - if (DEBUG) assert(blockInfo & FREE); // must be free - var size = blockInfo & ~TAGS_MASK; - if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be valid - - // mapping_insert - var fl: usize, sl: u32; - if (size < SB_SIZE) { - fl = 0; - sl = (size / AL_SIZE); - } else { - const inv: usize = sizeof() * 8 - 1; - fl = inv - clz(size); - sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); - fl -= SB_BITS - 1; - } - if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range - - // link previous and next free block - var prev = block.prev; - var next = block.next; - if (prev) prev.next = next; - if (next) next.prev = prev; - - // update head if we are removing it - if (block == GETHEAD(root, fl, sl)) { - SETHEAD(root, fl, sl, next); - - // clear second level map if head is empty now - if (!next) { - let slMap = GETSL(root, fl); - SETSL(root, fl, slMap &= ~(1 << sl)); - - // clear first level map if second level is empty now - if (!slMap) root.flMap &= ~(1 << fl); - } - } - // note: does not alter left/back because it is likely that splitting - // is performed afterwards, invalidating those changes. so, the caller - // must perform those updates. -} - -/** Searches for a free block of at least the specified size. */ -function searchBlock(root: Root, size: usize): Block | null { - // size was already asserted by caller - - // mapping_search - var fl: usize, sl: u32; - if (size < SB_SIZE) { - fl = 0; - sl = (size / AL_SIZE); - } else { - const halfMaxSize = BLOCK_MAXSIZE >> 1; // don't round last fl - const inv: usize = sizeof() * 8 - 1; - const invRound = inv - SL_BITS; - let requestSize = size < halfMaxSize - ? size + (1 << (invRound - clz(size))) - 1 - : size; - fl = inv - clz(requestSize); - sl = ((requestSize >> (fl - SL_BITS)) ^ (1 << SL_BITS)); - fl -= SB_BITS - 1; - } - if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range - - // search second level - var slMap = GETSL(root, fl) & (~0 << sl); - var head: Block | null; - if (!slMap) { - // search next larger first level - let flMap = root.flMap & (~0 << (fl + 1)); - if (!flMap) { - head = null; - } else { - fl = ctz(flMap); - slMap = GETSL(root, fl); - if (DEBUG) assert(slMap); // can't be zero if fl points here - head = GETHEAD(root, fl, ctz(slMap)); - } - } else { - head = GETHEAD(root, fl, ctz(slMap)); - } - return head; -} - -/** Prepares the specified block before (re-)use, possibly splitting it. */ -function prepareBlock(root: Root, block: Block, size: usize): void { - // size was already asserted by caller - - var blockInfo = block.mmInfo; - if (DEBUG) assert(!(size & AL_MASK)); // size must be aligned so the new block is - - // split if the block can hold another MINSIZE block incl. overhead - var remaining = (blockInfo & ~TAGS_MASK) - size; - if (remaining >= BLOCK_OVERHEAD + BLOCK_MINSIZE) { - block.mmInfo = size | (blockInfo & LEFTFREE); // also discards FREE - - let spare = changetype(changetype(block) + BLOCK_OVERHEAD + size); - spare.mmInfo = (remaining - BLOCK_OVERHEAD) | FREE; // not LEFTFREE - insertBlock(root, spare); // also sets 'back' - - // otherwise tag block as no longer FREE and right as no longer LEFTFREE - } else { - block.mmInfo = blockInfo & ~FREE; - GETRIGHT(block).mmInfo &= ~LEFTFREE; - } -} - -/** Adds more memory to the pool. */ -function addMemory(root: Root, start: usize, end: usize): bool { - if (DEBUG) { - assert( - start <= end && // must be valid - !(start & AL_MASK) && // must be aligned - !(end & AL_MASK) // must be aligned - ); - } - - var tail = GETTAIL(root); - var tailInfo: usize = 0; - if (tail) { // more memory - if (DEBUG) assert(start >= changetype(tail) + BLOCK_OVERHEAD); - - // merge with current tail if adjacent - if (start - BLOCK_OVERHEAD == changetype(tail)) { - start -= BLOCK_OVERHEAD; - tailInfo = tail.mmInfo; - } else { - // We don't do this, but a user might `memory.grow` manually - // leading to non-adjacent pages managed by TLSF. - } - - } else if (DEBUG) { // first memory - assert(start >= changetype(root) + ROOT_SIZE); // starts after root - } - - // check if size is large enough for a free block and the tail block - var size = end - start; - if (size < BLOCK_OVERHEAD + BLOCK_MINSIZE + BLOCK_OVERHEAD) { - return false; - } - - // left size is total minus its own and the zero-length tail's header - var leftSize = size - 2 * BLOCK_OVERHEAD; - var left = changetype(start); - left.mmInfo = leftSize | FREE | (tailInfo & LEFTFREE); - left.prev = null; - left.next = null; - - // tail is a zero-length used block - tail = changetype(start + size - BLOCK_OVERHEAD); - tail.mmInfo = 0 | LEFTFREE; - SETTAIL(root, tail); - - insertBlock(root, left); // also merges with free left before tail / sets 'back' - - return true; -} - -/** Grows memory to fit at least another block of the specified size. */ -function growMemory(root: Root, size: usize): void { - var pagesBefore = memory.size(); - var pagesNeeded = (((size + 0xffff) & ~0xffff) >>> 16); - var pagesWanted = max(pagesBefore, pagesNeeded); // double memory - if (memory.grow(pagesWanted) < 0) { - if (memory.grow(pagesNeeded) < 0) unreachable(); - } - var pagesAfter = memory.size(); - addMemory(root, pagesBefore << 16, pagesAfter << 16); -} - -/** Initilizes the root structure. */ -function initialize(): Root { - var rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK; - var pagesBefore = memory.size(); - var pagesNeeded = ((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16); - if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable(); - var root = changetype(rootOffset); - root.flMap = 0; - SETTAIL(root, changetype(0)); - for (let fl: usize = 0; fl < FL_BITS; ++fl) { - SETSL(root, fl, 0); - for (let sl: u32 = 0; sl < SL_SIZE; ++sl) { - SETHEAD(root, fl, sl, null); - } - } - addMemory(root, (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16); - return root; -} - -/** Prepares and checks an allocation size. */ -function prepareSize(size: usize): usize { - if (size >= BLOCK_MAXSIZE) throw new Error("allocation too large"); - return max((size + AL_MASK) & ~AL_MASK, BLOCK_MINSIZE); // align and ensure min size -} - -/** Allocates a block of the specified size. */ -function allocateBlock(root: Root, size: usize): Block { - var payloadSize = prepareSize(size); - var block = searchBlock(root, payloadSize); - if (!block) { - growMemory(root, payloadSize); - block = searchBlock(root, payloadSize); - if (DEBUG) assert(block); // must be found now - } - if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= payloadSize); // must fit - block.gcInfo = 0; - block.rtId = 0; // not determined yet - block.rtSize = size; - removeBlock(root, block); - prepareBlock(root, block, payloadSize); - return block; -} - -/** Reallocates a block to the specified size. */ -function reallocateBlock(root: Root, block: Block, size: usize): Block { - var payloadSize = prepareSize(size); - var blockInfo = block.mmInfo; - if (DEBUG) assert(!(blockInfo & FREE)); // must be used - - // possibly split and update runtime size if it still fits - if (payloadSize <= (blockInfo & ~TAGS_MASK)) { - prepareBlock(root, block, payloadSize); - block.rtSize = size; - return block; - } - - // merge with right free block if merger is large enough - var right = GETRIGHT(block); - var rightInfo = right.mmInfo; - if (rightInfo & FREE) { - let mergeSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK); - if (mergeSize >= payloadSize) { - removeBlock(root, right); - // TODO: this can yield an intermediate block larger than BLOCK_MAXSIZE, which - // is immediately split though. does this trigger any assertions / issues? - block.mmInfo = (blockInfo & TAGS_MASK) | mergeSize; - block.rtSize = size; - prepareBlock(root, block, payloadSize); - return block; - } - } - - // otherwise move the block - var newBlock = allocateBlock(root, size); - newBlock.gcInfo = block.gcInfo; - newBlock.rtId = block.rtId; - memory.copy(changetype(newBlock) + BLOCK_OVERHEAD, changetype(block) + BLOCK_OVERHEAD, size); - block.mmInfo = blockInfo | FREE; - insertBlock(root, block); - return newBlock; -} - -/** Frees a block. */ -function freeBlock(root: Root, block: Block): void { - var blockInfo = block.mmInfo; - assert(!(blockInfo & FREE)); // must be used (user might call through to this) - block.mmInfo = blockInfo | FREE; - insertBlock(root, block); -} - -// Memory manager interface. - -// @ts-ignore: decorator -@global @unsafe -function __mm_allocate(size: usize): usize { - var root = ROOT; - if (!root) ROOT = root = initialize(); - return changetype(allocateBlock(root, size)) + BLOCK_OVERHEAD; -} - -// @ts-ignore: decorator -@global @unsafe -function __mm_reallocate(data: usize, size: usize): usize { - if (DEBUG) assert(ROOT); // must be initialized - assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned - return changetype(reallocateBlock(ROOT, changetype(data - BLOCK_OVERHEAD), size)) + BLOCK_OVERHEAD; -} - -// @ts-ignore: decorator -@global @unsafe -function __mm_free(data: usize): void { - if (DEBUG) assert(ROOT); // must be initialized - assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned - freeBlock(ROOT, changetype(data - BLOCK_OVERHEAD)); -} - -/////////////////////////// A Pure Reference Counting Garbage Collector /////////////////////////// -// see: https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon03Pure.pdf - -// TODO: make visitors eat cookies so we can compile direct calls into a switch -function __rt_visit_members(s: Block, cookie: i32): void { unreachable(); } -function __rt_flags(classId: u32): u32 { return unreachable(); } -const ACYCLIC_FLAG: u32 = 0; - -// ╒══════════════════════ GC Info structure ══════════════════════╕ -// │ 3 2 1 │ -// │1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0│ -// ├─┼─┴─┴─┼─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ -// │B│color│ refCount │ -// └─┴─────┴───────────────────────────────────────────────────────┘ -// B: buffered - -// @ts-ignore: decorator -@inline const BUFFERED_MASK: u32 = 1 << (sizeof() * 8 - 1); -// @ts-ignore: decorator -@inline const COLOR_BITS = 3; -// @ts-ignore: decorator -@inline const COLOR_SHIFT: u32 = ctz(BUFFERED_MASK) - COLOR_BITS; -// @ts-ignore: decorator -@inline const COLOR_MASK: u32 = ((1 << COLOR_BITS) - 1) << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const REFCOUNT_MASK: u32 = (1 << COLOR_SHIFT) - 1; - -// ╒════════╤═══════════════════ Colors ═══════════════════════════╕ -// │ Color │ Meaning │ -// ├────────┼──────────────────────────────────────────────────────┤ -// │ BLACK │ In use or free │ -// │ GRAY │ Possible member of cycle │ -// │ WHITE │ Member of garbage cycle │ -// │ PURPLE │ Possible root of cycle │ -// │ RED │ Candidate cycle undergoing Σ-computation *concurrent │ -// │ ORANGE │ Candidate cycle awaiting epoch boundary *concurrent │ -// └────────┴──────────────────────────────────────────────────────┘ -// Acyclic detection has been decoupled, hence no GREEN. - -// @ts-ignore: decorator -@inline const COLOR_BLACK: u32 = 0 << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const COLOR_GRAY: u32 = 1 << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const COLOR_WHITE: u32 = 2 << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const COLOR_PURPLE: u32 = 3 << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const COLOR_RED: u32 = 4 << COLOR_SHIFT; -// @ts-ignore: decorator -@inline const COLOR_ORANGE: u32 = 5 << COLOR_SHIFT; - -// @ts-ignore: decorator -@inline const VISIT_DECREMENT = 1; // guard 0 -// @ts-ignore: decorator -@inline const VISIT_MARKGRAY = 2; -// @ts-ignore: decorator -@inline const VISIT_SCAN = 3; -// @ts-ignore: decorator -@inline const VISIT_SCANBLACK = 4; -// @ts-ignore: decorator -@inline const VISIT_COLLECTWHITE = 5; - -// @ts-ignore: decorator -@global -function __rt_visit(s: Block, cookie: i32): void { - switch (cookie) { - case VISIT_DECREMENT: { - decrement(s); - break; - } - case VISIT_MARKGRAY: { - if (DEBUG) assert((s.gcInfo & REFCOUNT_MASK) > 0); - s.gcInfo = s.gcInfo - 1; - markGray(s); - break; - } - case VISIT_SCAN: { - scan(s); - break; - } - case VISIT_SCANBLACK: { - let info = s.gcInfo; - assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow - s.gcInfo = info + 1; - if ((info & COLOR_MASK) != COLOR_BLACK) { - scanBlack(s); - } - break; - } - case VISIT_COLLECTWHITE: { - collectWhite(s); - break; - } - default: if (DEBUG) assert(false); - } -} - -/** Increments the reference count of the specified block by one.*/ -function increment(s: Block): void { - var info = s.gcInfo; - assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow - s.gcInfo = info + 1; -} - -/** Decrements the reference count of the specified block by one, possibly freeing it. */ -function decrement(s: Block): void { - var info = s.gcInfo; - var rc = info & REFCOUNT_MASK; - if (rc == 1) { - __rt_visit_members(s, VISIT_DECREMENT); - if (!(info & BUFFERED_MASK)) { - freeBlock(ROOT, s); - } else { - s.gcInfo = BUFFERED_MASK | COLOR_BLACK | 0; - } - } else { - if (DEBUG) assert(rc > 0); - if (!(__rt_flags(s.rtId) & ACYCLIC_FLAG)) { - s.gcInfo = BUFFERED_MASK | COLOR_PURPLE | (rc - 1); - if (!(info & BUFFERED_MASK)) { - appendRoot(s); - } - } else { - s.gcInfo = (info & ~REFCOUNT_MASK) | (rc - 1); - } - } -} - -/** Buffer of possible roots. */ -// @ts-ignore: decorator -@lazy var ROOTS: usize; -/** Current absolute offset into the `ROOTS` buffer. */ -// @ts-ignore: decorator -@lazy var CUR: usize = 0; -/** Current absolute end offset into the `ROOTS` buffer. */ -// @ts-ignore: decorator -@lazy var END: usize = 0; - -/** Appends a block to possible roots. */ -function appendRoot(s: Block): void { - var cur = CUR; - if (cur >= END) { - growRoots(); // TBD: either that or pick a default and force collection on overflow - cur = CUR; - } - store(cur, s); - CUR = cur + 1; -} - -/** Grows the roots buffer if it ran full. */ -function growRoots(): void { - var oldRoots = ROOTS; - var oldSize = CUR - oldRoots; - var newSize = max(oldSize * 2, 64 << alignof()); - var newRoots = memory.allocate(newSize); - memory.copy(newRoots, oldRoots, oldSize); - ROOTS = newRoots; - CUR = newRoots + oldSize; - END = newRoots + newSize; -} - -/** Collects cyclic garbage. */ -function collectCycles(): void { - - // markRoots - var roots = ROOTS; - var cur = roots; - for (let pos = cur, end = CUR; pos < end; pos += sizeof()) { - let s = load(pos); - let info = s.gcInfo; - if ((info & COLOR_MASK) == COLOR_PURPLE && (info & REFCOUNT_MASK) > 0) { - markGray(s); - store(cur, s); - cur += sizeof(); - } else { - if ((info & COLOR_MASK) == COLOR_BLACK && !(info & REFCOUNT_MASK)) { - freeBlock(ROOT, s); - } else { - s.gcInfo = info & ~BUFFERED_MASK; - } - } - } - CUR = cur; - - // scanRoots - for (let pos = roots; pos < cur; pos += sizeof()) { - scan(load(pos)); - } - - // collectRoots - for (let pos = roots; pos < cur; pos += sizeof()) { - let s = load(pos); - s.gcInfo = s.gcInfo & ~BUFFERED_MASK; - collectWhite(s); - } - CUR = roots; -} - -/** Marks a block as gray (possible member of cycle) during the collection phase. */ -function markGray(s: Block): void { - var info = s.gcInfo; - if ((info & COLOR_MASK) != COLOR_GRAY) { - s.gcInfo = (info & ~COLOR_MASK) | COLOR_GRAY; - __rt_visit_members(s, VISIT_MARKGRAY); - } -} - -/** Scans a block during the collection phase, determining whether it is garbage or not. */ -function scan(s: Block): void { - var info = s.gcInfo; - if ((info & COLOR_MASK) == COLOR_GRAY) { - if ((info & REFCOUNT_MASK) > 0) { - scanBlack(s); - } else { - s.gcInfo = (info & ~COLOR_MASK) | COLOR_WHITE; - __rt_visit_members(s, VISIT_SCAN); - } - } -} - -/** Marks a block as black (in use) if it was found to be reachable during the collection phase. */ -function scanBlack(s: Block): void { - s.gcInfo = (s.gcInfo & ~COLOR_MASK) | COLOR_BLACK; - __rt_visit_members(s, VISIT_SCANBLACK); -} - -/** Collects all white (member of a garbage cycle) nodes when completing the collection phase. */ -function collectWhite(s: Block): void { - var info = s.gcInfo; - if ((info & COLOR_MASK) == COLOR_WHITE && !(info & BUFFERED_MASK)) { - // s.gcInfo = (info & ~COLOR_MASK) | COLOR_BLACK; - __rt_visit_members(s, VISIT_COLLECTWHITE); - } - freeBlock(ROOT, s); -} - -// Garbage collector interface - -// @ts-ignore: decorator -@global @unsafe -function __gc_retain(ref: usize): void { - if (ref) increment(changetype(ref - BLOCK_OVERHEAD)); -} - -// @ts-ignore: decorator -@global @unsafe -function __gc_release(ref: usize): void { - if (ref) decrement(changetype(ref - BLOCK_OVERHEAD)); -} - -// keep alive, everything else is reached from here export { __mm_allocate, __mm_reallocate, __mm_free, - __rt_visit, __gc_retain, __gc_release, - collectCycles as __gc_collect + __gc_collect }; -// @start export function main(): void {} +@start export function main(): void {} diff --git a/tests/runtime/index.html b/tests/runtime/index.html index d6c79dbb..d029c223 100644 --- a/tests/runtime/index.html +++ b/tests/runtime/index.html @@ -133,6 +133,7 @@ function allocate(size) { el.appendChild(er); var ef = document.createElement("button"); ef.innerText = "free"; + ef.className = "free"; el.appendChild(ef); ef.onclick = function() { exports.__mm_free(ptr); @@ -154,12 +155,16 @@ button:hover { background: #bbb; } .clear { clear: both; } /* Lists */ .fl, .sl, .hl, .seg { float: left; padding: 0.4em; margin: 0.2em; border: 1px solid #ddd; border-radius: 3px; } +.fl { min-width: 1.3em; text-align: center; } .sl { min-width: 12em; } .hl { min-width: 7em; font-size: 0.8em; } .num { color: #fff; background: rgba(0, 0, 0, 0.3); padding: 0.4em; margin-left: -0.4em; border-radius: 0.2em; } .set { background: #7f7; } +.seg { border-top: 0.3em solid #333; } .seg button { margin-left: 0.5em; } .sub { vertical-align: sub; font-size: 0.8em; } +.free { background: #f77; border-color: #c33; } +.free:hover { background: #c33; color: #fff; }

AssemblyScript Runtime Visualizer / TLSF

diff --git a/tests/runtime/optimized.wat b/tests/runtime/optimized.wat index 5b8b4477..f35cbe3e 100644 --- a/tests/runtime/optimized.wat +++ b/tests/runtime/optimized.wat @@ -1,31 +1,39 @@ (module + (type $FUNCSIG$v (func)) (type $FUNCSIG$ii (func (param i32) (result i32))) - (type $FUNCSIG$i (func (result i32))) (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) (type $FUNCSIG$vii (func (param i32 i32))) (type $FUNCSIG$iii (func (param i32 i32) (result i32))) (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$viii (func (param i32 i32 i32))) (type $FUNCSIG$vi (func (param i32))) - (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) - (data (i32.const 8) "\10\00\00\00\"") - (data (i32.const 24) "a\00s\00s\00e\00m\00b\00l\00y\00/\00i\00n\00d\00e\00x\00.\00t\00s") - (data (i32.const 64) "\10\00\00\00\1c") - (data (i32.const 80) "~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s") - (global $assembly/index/ROOT (mut i32) (i32.const 0)) - (global $assembly/index/CUR (mut i32) (i32.const 0)) - (global $assembly/index/ROOTS (mut i32) (i32.const 0)) + (data (i32.const 8) "\10\00\00\00\1e") + (data (i32.const 24) "~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") + (data (i32.const 56) "\10\00\00\00\1c") + (data (i32.const 72) "~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s") + (global $~lib/started (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/rt/pure/CUR (mut i32) (i32.const 0)) + (global $~lib/rt/pure/ROOTS (mut i32) (i32.const 0)) (export "memory" (memory $0)) - (export "__mm_allocate" (func $assembly/index/__mm_allocate)) - (export "__mm_reallocate" (func $assembly/index/__mm_reallocate)) - (export "__mm_free" (func $assembly/index/__mm_free)) - (export "__rt_visit" (func $assembly/index/__rt_visit)) - (export "__gc_retain" (func $assembly/index/__gc_retain)) - (export "__gc_release" (func $assembly/index/__gc_release)) - (export "__gc_collect" (func $assembly/index/collectCycles)) - (func $assembly/index/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (export "main" (func $assembly/index/main)) + (export "__mm_allocate" (func $~lib/rt/index/__mm_allocate)) + (export "__mm_reallocate" (func $~lib/rt/index/__mm_reallocate)) + (export "__mm_free" (func $~lib/rt/index/__mm_free)) + (export "__gc_retain" (func $~lib/rt/index/__gc_retain)) + (export "__gc_release" (func $~lib/rt/index/__gc_release)) + (export "__gc_collect" (func $~lib/rt/index/__gc_collect)) + (func $assembly/index/main (; 1 ;) (type $FUNCSIG$v) + global.get $~lib/started + i32.eqz + if + i32.const 1 + global.set $~lib/started + end + ) + (func $~lib/rt/tlsf/removeBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -141,7 +149,7 @@ end end ) - (func $assembly/index/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/insertBlock (; 3 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -180,7 +188,7 @@ if local.get $0 local.get $4 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $2 i32.const 3 @@ -227,7 +235,7 @@ if local.get $0 local.get $3 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $3 local.get $6 i32.const 3 @@ -339,7 +347,7 @@ i32.or i32.store offset=4 ) - (func $assembly/index/addMemory (; 3 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/addMemory (; 4 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) local.get $2 block (result i32) @@ -401,9 +409,9 @@ i32.store offset=1568 local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) - (func $assembly/index/initialize (; 4 ;) (type $FUNCSIG$i) (result i32) + (func $~lib/rt/tlsf/initializeRoot (; 5 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -495,10 +503,11 @@ current_memory i32.const 16 i32.shl - call $assembly/index/addMemory + call $~lib/rt/tlsf/addMemory local.get $0 + global.set $~lib/rt/tlsf/ROOT ) - (func $assembly/index/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/tlsf/prepareSize (; 6 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) local.get $0 i32.const 1073741824 @@ -506,7 +515,7 @@ if i32.const 0 i32.const 24 - i32.const 466 + i32.const 436 i32.const 29 call $~lib/builtins/abort unreachable @@ -524,7 +533,7 @@ i32.gt_u select ) - (func $assembly/index/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/searchBlock (; 7 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) local.get $0 local.get $1 @@ -625,7 +634,7 @@ end end ) - (func $assembly/index/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/growMemory (; 8 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -664,9 +673,9 @@ current_memory i32.const 16 i32.shl - call $assembly/index/addMemory + call $~lib/rt/tlsf/addMemory ) - (func $assembly/index/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/prepareBlock (; 9 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) local.get $1 @@ -701,7 +710,7 @@ i32.store local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock else local.get $1 local.get $3 @@ -730,23 +739,23 @@ i32.store end ) - (func $assembly/index/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/allocateBlock (; 10 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) local.get $0 local.get $1 - call $assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.tee $3 - call $assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.tee $2 i32.eqz if local.get $0 local.get $3 - call $assembly/index/growMemory + call $~lib/rt/tlsf/growMemory local.get $0 local.get $3 - call $assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $2 end local.get $2 @@ -760,30 +769,29 @@ i32.store offset=12 local.get $0 local.get $2 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $0 local.get $2 local.get $3 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $2 ) - (func $assembly/index/__mm_allocate (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/index/__mm_allocate (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.tee $1 - i32.eqz - if - call $assembly/index/initialize - local.tee $1 - global.set $assembly/index/ROOT + if (result i32) + local.get $1 + else + call $~lib/rt/tlsf/initializeRoot + global.get $~lib/rt/tlsf/ROOT end - local.get $1 local.get $0 - call $assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock i32.const 16 i32.add ) - (func $~lib/memory/memory.copy (; 11 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/memory/memory.copy (; 12 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) block $~lib/util/memory/memmove|inlined.0 local.get $0 @@ -957,13 +965,13 @@ end end ) - (func $assembly/index/reallocateBlock (; 12 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $~lib/rt/tlsf/reallocateBlock (; 13 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) (local $6 i32) local.get $2 - call $assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.tee $3 local.get $1 i32.load @@ -975,7 +983,7 @@ local.get $0 local.get $1 local.get $3 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $1 local.get $2 i32.store offset=12 @@ -1011,7 +1019,7 @@ if local.get $0 local.get $6 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $4 i32.const 3 @@ -1025,14 +1033,14 @@ local.get $0 local.get $1 local.get $3 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $1 return end end local.get $0 local.get $2 - call $assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock local.tee $3 local.get $1 i32.load offset=4 @@ -1056,20 +1064,20 @@ i32.store local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock local.get $3 ) - (func $assembly/index/__mm_reallocate (; 13 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) - global.get $assembly/index/ROOT + (func $~lib/rt/index/__mm_reallocate (; 14 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + global.get $~lib/rt/tlsf/ROOT local.get $0 i32.const 16 i32.sub local.get $1 - call $assembly/index/reallocateBlock + call $~lib/rt/tlsf/reallocateBlock i32.const 16 i32.add ) - (func $assembly/index/freeBlock (; 14 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/freeBlock (; 15 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) local.get $1 local.get $1 i32.load @@ -1078,31 +1086,50 @@ i32.store local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) - (func $assembly/index/__mm_free (; 15 ;) (type $FUNCSIG$vi) (param $0 i32) - global.get $assembly/index/ROOT + (func $~lib/rt/index/__mm_free (; 16 ;) (type $FUNCSIG$vi) (param $0 i32) + global.get $~lib/rt/tlsf/ROOT local.get $0 i32.const 16 i32.sub - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock ) - (func $assembly/index/decrement (; 16 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/index/__gc_retain (; 17 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 - i32.load offset=4 - i32.const 268435455 - i32.and - i32.const 1 - i32.eq - i32.eqz if local.get $0 - i32.load offset=8 - drop + i32.const 16 + i32.sub + local.tee $0 + local.get $0 + i32.load offset=4 + i32.const 1 + i32.add + i32.store offset=4 end - unreachable ) - (func $assembly/index/markGray (; 17 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/index/__gc_release (; 18 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + local.tee $0 + i32.load offset=4 + i32.const 268435455 + i32.and + i32.const 1 + i32.ne + if + local.get $0 + i32.load offset=8 + drop + end + unreachable + end + ) + (func $~lib/rt/pure/markGray (; 19 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -1122,16 +1149,7 @@ unreachable end ) - (func $assembly/index/scanBlack (; 18 ;) (type $FUNCSIG$vi) (param $0 i32) - local.get $0 - local.get $0 - i32.load offset=4 - i32.const -1879048193 - i32.and - i32.store offset=4 - unreachable - ) - (func $assembly/index/scan (; 19 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/scan (; 20 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -1148,7 +1166,11 @@ i32.gt_u if local.get $0 - call $assembly/index/scanBlack + local.get $0 + i32.load offset=4 + i32.const -1879048193 + i32.and + i32.store offset=4 else local.get $0 local.get $1 @@ -1157,11 +1179,11 @@ i32.const 536870912 i32.or i32.store offset=4 - unreachable end + unreachable end ) - (func $assembly/index/collectWhite (; 20 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/collectWhite (; 21 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -1179,95 +1201,22 @@ if unreachable end - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock ) - (func $assembly/index/__rt_visit (; 21 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) - block $break|0 - block $case4|0 - block $case3|0 - block $case2|0 - block $case1|0 - block $case0|0 - local.get $1 - i32.const 1 - i32.sub - br_table $case0|0 $case1|0 $case2|0 $case3|0 $case4|0 $break|0 - end - local.get $0 - call $assembly/index/decrement - br $break|0 - end - local.get $0 - local.get $0 - i32.load offset=4 - i32.const 1 - i32.sub - i32.store offset=4 - local.get $0 - call $assembly/index/markGray - br $break|0 - end - local.get $0 - call $assembly/index/scan - br $break|0 - end - local.get $0 - local.get $0 - i32.load offset=4 - local.tee $1 - i32.const 1 - i32.add - i32.store offset=4 - local.get $1 - i32.const 1879048192 - i32.and - if - local.get $0 - call $assembly/index/scanBlack - end - br $break|0 - end - local.get $0 - call $assembly/index/collectWhite - end - ) - (func $assembly/index/__gc_retain (; 22 ;) (type $FUNCSIG$vi) (param $0 i32) - local.get $0 - if - local.get $0 - i32.const 16 - i32.sub - local.tee $0 - local.get $0 - i32.load offset=4 - i32.const 1 - i32.add - i32.store offset=4 - end - ) - (func $assembly/index/__gc_release (; 23 ;) (type $FUNCSIG$vi) (param $0 i32) - local.get $0 - if - local.get $0 - i32.const 16 - i32.sub - call $assembly/index/decrement - end - ) - (func $assembly/index/collectCycles (; 24 ;) (type $FUNCSIG$v) + (func $~lib/rt/pure/collectCycles (; 22 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) - global.get $assembly/index/ROOTS + global.get $~lib/rt/pure/ROOTS local.tee $5 local.tee $2 local.set $3 - global.get $assembly/index/CUR + global.get $~lib/rt/pure/CUR local.set $0 loop $repeat|0 block $break|0 @@ -1293,7 +1242,7 @@ select if local.get $4 - call $assembly/index/markGray + call $~lib/rt/pure/markGray local.get $2 local.get $4 i32.store @@ -1312,9 +1261,9 @@ i32.and select if - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $4 - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock else local.get $4 local.get $1 @@ -1331,7 +1280,7 @@ end end local.get $2 - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR local.get $5 local.set $0 loop $repeat|1 @@ -1342,7 +1291,7 @@ br_if $break|1 local.get $0 i32.load - call $assembly/index/scan + call $~lib/rt/pure/scan local.get $0 i32.const 4 i32.add @@ -1367,7 +1316,7 @@ i32.and i32.store offset=4 local.get $1 - call $assembly/index/collectWhite + call $~lib/rt/pure/collectWhite local.get $0 i32.const 4 i32.add @@ -1376,9 +1325,12 @@ end end local.get $5 - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR ) - (func $null (; 25 ;) (type $FUNCSIG$v) + (func $~lib/rt/index/__gc_collect (; 23 ;) (type $FUNCSIG$v) + call $~lib/rt/pure/collectCycles + ) + (func $start (; 24 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/runtime/untouched.wasm b/tests/runtime/untouched.wasm index d5fdae90..b84c7aa0 100644 Binary files a/tests/runtime/untouched.wasm and b/tests/runtime/untouched.wasm differ diff --git a/tests/runtime/untouched.wat b/tests/runtime/untouched.wat index 5b5a1d4c..e3f74648 100644 --- a/tests/runtime/untouched.wat +++ b/tests/runtime/untouched.wat @@ -1,34 +1,45 @@ (module + (type $FUNCSIG$v (func)) (type $FUNCSIG$ii (func (param i32) (result i32))) - (type $FUNCSIG$i (func (result i32))) (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$vii (func (param i32 i32))) (type $FUNCSIG$iii (func (param i32 i32) (result i32))) (type $FUNCSIG$viii (func (param i32 i32 i32))) (type $FUNCSIG$vi (func (param i32))) - (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) - (data (i32.const 8) "\10\00\00\00\"\00\00\00\00\00\00\00\00\00\00\00a\00s\00s\00e\00m\00b\00l\00y\00/\00i\00n\00d\00e\00x\00.\00t\00s\00") - (data (i32.const 64) "\10\00\00\00\1c\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s\00") + (data (i32.const 8) "\10\00\00\00\1e\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00") + (data (i32.const 56) "\10\00\00\00 \00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00n\00d\00e\00x\00.\00t\00s\00") + (data (i32.const 104) "\10\00\00\00\1e\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00p\00u\00r\00e\00.\00t\00s\00") + (data (i32.const 152) "\10\00\00\00\1c\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s\00") (table $0 1 funcref) (elem (i32.const 0) $null) - (global $assembly/index/ROOT (mut i32) (i32.const 0)) - (global $assembly/index/ACYCLIC_FLAG i32 (i32.const 0)) - (global $assembly/index/CUR (mut i32) (i32.const 0)) - (global $assembly/index/END (mut i32) (i32.const 0)) - (global $assembly/index/ROOTS (mut i32) (i32.const 0)) - (global $~lib/memory/HEAP_BASE i32 (i32.const 108)) + (global $~lib/rt/pure/ACYCLIC_FLAG i32 (i32.const 0)) + (global $~lib/started (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/rt/pure/CUR (mut i32) (i32.const 0)) + (global $~lib/rt/pure/END (mut i32) (i32.const 0)) + (global $~lib/rt/pure/ROOTS (mut i32) (i32.const 0)) + (global $~lib/memory/HEAP_BASE i32 (i32.const 196)) (export "memory" (memory $0)) - (export "__mm_allocate" (func $assembly/index/__mm_allocate)) - (export "__mm_reallocate" (func $assembly/index/__mm_reallocate)) - (export "__mm_free" (func $assembly/index/__mm_free)) - (export "__rt_visit" (func $assembly/index/__rt_visit)) - (export "__gc_retain" (func $assembly/index/__gc_retain)) - (export "__gc_release" (func $assembly/index/__gc_release)) - (export "__gc_collect" (func $assembly/index/collectCycles)) - (func $assembly/index/removeBlock (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (export "main" (func $assembly/index/main)) + (export "__mm_allocate" (func $~lib/rt/index/__mm_allocate)) + (export "__mm_reallocate" (func $~lib/rt/index/__mm_reallocate)) + (export "__mm_free" (func $~lib/rt/index/__mm_free)) + (export "__gc_retain" (func $~lib/rt/index/__gc_retain)) + (export "__gc_release" (func $~lib/rt/index/__gc_release)) + (export "__gc_collect" (func $~lib/rt/index/__gc_collect)) + (func $assembly/index/main (; 1 ;) (type $FUNCSIG$v) + global.get $~lib/started + i32.eqz + if + call $start + i32.const 1 + global.set $~lib/started + end + ) + (func $~lib/rt/tlsf/removeBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -49,7 +60,7 @@ if i32.const 0 i32.const 24 - i32.const 276 + i32.const 265 i32.const 13 call $~lib/builtins/abort unreachable @@ -74,7 +85,7 @@ if i32.const 0 i32.const 24 - i32.const 278 + i32.const 267 i32.const 13 call $~lib/builtins/abort unreachable @@ -126,7 +137,7 @@ if i32.const 0 i32.const 24 - i32.const 291 + i32.const 280 i32.const 13 call $~lib/builtins/abort unreachable @@ -150,7 +161,7 @@ i32.store offset=16 end local.get $1 - block $assembly/index/GETHEAD|inlined.1 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.1 (result i32) local.get $0 local.set $10 local.get $4 @@ -170,7 +181,7 @@ end i32.eq if - block $assembly/index/SETHEAD|inlined.1 + block $~lib/rt/tlsf/SETHEAD|inlined.1 local.get $0 local.set $11 local.get $4 @@ -194,7 +205,7 @@ local.get $7 i32.eqz if - block $assembly/index/GETSL|inlined.0 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.0 (result i32) local.get $0 local.set $9 local.get $4 @@ -207,7 +218,7 @@ i32.load offset=4 end local.set $8 - block $assembly/index/SETSL|inlined.1 + block $~lib/rt/tlsf/SETSL|inlined.1 local.get $0 local.set $11 local.get $4 @@ -246,7 +257,7 @@ end end ) - (func $assembly/index/insertBlock (; 2 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/insertBlock (; 3 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -264,7 +275,7 @@ if i32.const 0 i32.const 24 - i32.const 204 + i32.const 193 i32.const 13 call $~lib/builtins/abort unreachable @@ -279,12 +290,12 @@ if i32.const 0 i32.const 24 - i32.const 206 + i32.const 195 i32.const 13 call $~lib/builtins/abort unreachable end - block $assembly/index/GETRIGHT|inlined.0 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.0 (result i32) local.get $1 local.set $3 local.get $3 @@ -326,7 +337,7 @@ if local.get $0 local.get $4 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $2 i32.const 3 @@ -335,7 +346,7 @@ i32.or local.tee $2 i32.store - block $assembly/index/GETRIGHT|inlined.1 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.1 (result i32) local.get $1 local.set $6 local.get $6 @@ -359,7 +370,7 @@ i32.const 2 i32.and if - block $assembly/index/GETFREELEFT|inlined.0 (result i32) + block $~lib/rt/tlsf/GETFREELEFT|inlined.0 (result i32) local.get $1 local.set $3 local.get $3 @@ -378,7 +389,7 @@ if i32.const 0 i32.const 24 - i32.const 227 + i32.const 216 i32.const 15 call $~lib/builtins/abort unreachable @@ -403,7 +414,7 @@ if local.get $0 local.get $3 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $3 local.get $6 i32.const 3 @@ -441,7 +452,7 @@ if i32.const 0 i32.const 24 - i32.const 242 + i32.const 231 i32.const 13 call $~lib/builtins/abort unreachable @@ -457,7 +468,7 @@ if i32.const 0 i32.const 24 - i32.const 243 + i32.const 232 i32.const 13 call $~lib/builtins/abort unreachable @@ -514,12 +525,12 @@ if i32.const 0 i32.const 24 - i32.const 259 + i32.const 248 i32.const 13 call $~lib/builtins/abort unreachable end - block $assembly/index/GETHEAD|inlined.2 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.2 (result i32) local.get $0 local.set $3 local.get $9 @@ -550,7 +561,7 @@ local.get $1 i32.store offset=16 end - block $assembly/index/SETHEAD|inlined.2 + block $~lib/rt/tlsf/SETHEAD|inlined.2 local.get $0 local.set $12 local.get $9 @@ -579,8 +590,8 @@ i32.shl i32.or i32.store - block $assembly/index/SETSL|inlined.2 - block $assembly/index/GETSL|inlined.1 (result i32) + block $~lib/rt/tlsf/SETSL|inlined.2 + block $~lib/rt/tlsf/GETSL|inlined.1 (result i32) local.get $0 local.set $13 local.get $9 @@ -606,7 +617,7 @@ i32.store offset=4 end ) - (func $assembly/index/addMemory (; 3 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $~lib/rt/tlsf/addMemory (; 4 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -637,12 +648,12 @@ if i32.const 0 i32.const 24 - i32.const 385 + i32.const 374 i32.const 4 call $~lib/builtins/abort unreachable end - block $assembly/index/GETTAIL|inlined.0 (result i32) + block $~lib/rt/tlsf/GETTAIL|inlined.0 (result i32) local.get $0 local.set $3 local.get $3 @@ -662,7 +673,7 @@ if i32.const 0 i32.const 24 - i32.const 395 + i32.const 384 i32.const 15 call $~lib/builtins/abort unreachable @@ -693,7 +704,7 @@ if i32.const 0 i32.const 24 - i32.const 407 + i32.const 396 i32.const 4 call $~lib/builtins/abort unreachable @@ -748,7 +759,7 @@ i32.const 2 i32.or i32.store - block $assembly/index/SETTAIL|inlined.1 + block $~lib/rt/tlsf/SETTAIL|inlined.1 local.get $0 local.set $9 local.get $4 @@ -759,10 +770,10 @@ end local.get $0 local.get $8 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock i32.const 1 ) - (func $assembly/index/initialize (; 4 ;) (type $FUNCSIG$i) (result i32) + (func $~lib/rt/tlsf/initializeRoot (; 5 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -816,7 +827,7 @@ local.get $3 i32.const 0 i32.store - block $assembly/index/SETTAIL|inlined.0 + block $~lib/rt/tlsf/SETTAIL|inlined.0 local.get $3 local.set $5 i32.const 0 @@ -835,7 +846,7 @@ i32.eqz br_if $break|0 block - block $assembly/index/SETSL|inlined.0 + block $~lib/rt/tlsf/SETSL|inlined.0 local.get $3 local.set $7 local.get $4 @@ -859,7 +870,7 @@ i32.lt_u i32.eqz br_if $break|1 - block $assembly/index/SETHEAD|inlined.0 + block $~lib/rt/tlsf/SETHEAD|inlined.0 local.get $3 local.set $9 local.get $4 @@ -912,11 +923,12 @@ current_memory i32.const 16 i32.shl - call $assembly/index/addMemory + call $~lib/rt/tlsf/addMemory drop local.get $3 + global.set $~lib/rt/tlsf/ROOT ) - (func $assembly/index/prepareSize (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/tlsf/prepareSize (; 6 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) (local $2 i32) local.get $0 @@ -925,7 +937,7 @@ if i32.const 0 i32.const 24 - i32.const 466 + i32.const 436 i32.const 29 call $~lib/builtins/abort unreachable @@ -945,7 +957,7 @@ i32.gt_u select ) - (func $assembly/index/searchBlock (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/searchBlock (; 7 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -1019,12 +1031,12 @@ if i32.const 0 i32.const 24 - i32.const 337 + i32.const 326 i32.const 13 call $~lib/builtins/abort unreachable end - block $assembly/index/GETSL|inlined.2 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.2 (result i32) local.get $0 local.set $5 local.get $2 @@ -1066,7 +1078,7 @@ local.get $4 i32.ctz local.set $2 - block $assembly/index/GETSL|inlined.3 (result i32) + block $~lib/rt/tlsf/GETSL|inlined.3 (result i32) local.get $0 local.set $8 local.get $2 @@ -1084,12 +1096,12 @@ if i32.const 0 i32.const 24 - i32.const 350 + i32.const 339 i32.const 17 call $~lib/builtins/abort unreachable end - block $assembly/index/GETHEAD|inlined.3 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.3 (result i32) local.get $0 local.set $9 local.get $2 @@ -1111,7 +1123,7 @@ local.set $7 end else - block $assembly/index/GETHEAD|inlined.4 (result i32) + block $~lib/rt/tlsf/GETHEAD|inlined.4 (result i32) local.get $0 local.set $8 local.get $2 @@ -1134,7 +1146,7 @@ end local.get $7 ) - (func $assembly/index/growMemory (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/growMemory (; 8 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -1184,10 +1196,10 @@ local.get $7 i32.const 16 i32.shl - call $assembly/index/addMemory + call $~lib/rt/tlsf/addMemory drop ) - (func $assembly/index/prepareBlock (; 8 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/rt/tlsf/prepareBlock (; 9 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -1202,7 +1214,7 @@ if i32.const 0 i32.const 24 - i32.const 364 + i32.const 353 i32.const 13 call $~lib/builtins/abort unreachable @@ -1243,7 +1255,7 @@ i32.store local.get $0 local.get $5 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock else local.get $1 local.get $3 @@ -1252,7 +1264,7 @@ i32.xor i32.and i32.store - block $assembly/index/GETRIGHT|inlined.3 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.3 (result i32) local.get $1 local.set $5 local.get $5 @@ -1266,7 +1278,7 @@ i32.and i32.add end - block $assembly/index/GETRIGHT|inlined.2 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.2 (result i32) local.get $1 local.set $5 local.get $5 @@ -1288,32 +1300,32 @@ i32.store end ) - (func $assembly/index/allocateBlock (; 9 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/rt/tlsf/allocateBlock (; 10 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) local.get $1 - call $assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.set $2 local.get $0 local.get $2 - call $assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $3 local.get $3 i32.eqz if local.get $0 local.get $2 - call $assembly/index/growMemory + call $~lib/rt/tlsf/growMemory local.get $0 local.get $2 - call $assembly/index/searchBlock + call $~lib/rt/tlsf/searchBlock local.set $3 local.get $3 i32.eqz if i32.const 0 i32.const 24 - i32.const 477 + i32.const 466 i32.const 15 call $~lib/builtins/abort unreachable @@ -1331,7 +1343,7 @@ if i32.const 0 i32.const 24 - i32.const 479 + i32.const 468 i32.const 13 call $~lib/builtins/abort unreachable @@ -1347,31 +1359,31 @@ i32.store offset=12 local.get $0 local.get $3 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $0 local.get $3 local.get $2 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $3 ) - (func $assembly/index/__mm_allocate (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/index/__mm_allocate (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.set $1 local.get $1 i32.eqz if - call $assembly/index/initialize - local.tee $1 - global.set $assembly/index/ROOT + call $~lib/rt/tlsf/initializeRoot + global.get $~lib/rt/tlsf/ROOT + local.set $1 end local.get $1 local.get $0 - call $assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock i32.const 16 i32.add ) - (func $~lib/memory/memory.copy (; 11 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/memory/memory.copy (; 12 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -1580,7 +1592,7 @@ end end ) - (func $assembly/index/reallocateBlock (; 12 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $~lib/rt/tlsf/reallocateBlock (; 13 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -1588,7 +1600,7 @@ (local $7 i32) (local $8 i32) local.get $2 - call $assembly/index/prepareSize + call $~lib/rt/tlsf/prepareSize local.set $3 local.get $1 i32.load @@ -1601,7 +1613,7 @@ if i32.const 0 i32.const 24 - i32.const 492 + i32.const 481 i32.const 13 call $~lib/builtins/abort unreachable @@ -1617,14 +1629,14 @@ local.get $0 local.get $1 local.get $3 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $1 local.get $2 i32.store offset=12 local.get $1 return end - block $assembly/index/GETRIGHT|inlined.4 (result i32) + block $~lib/rt/tlsf/GETRIGHT|inlined.4 (result i32) local.get $1 local.set $5 local.get $5 @@ -1666,7 +1678,7 @@ if local.get $0 local.get $6 - call $assembly/index/removeBlock + call $~lib/rt/tlsf/removeBlock local.get $1 local.get $4 i32.const 3 @@ -1680,14 +1692,14 @@ local.get $0 local.get $1 local.get $3 - call $assembly/index/prepareBlock + call $~lib/rt/tlsf/prepareBlock local.get $1 return end end local.get $0 local.get $2 - call $assembly/index/allocateBlock + call $~lib/rt/tlsf/allocateBlock local.set $8 local.get $8 local.get $1 @@ -1712,16 +1724,16 @@ i32.store local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock local.get $8 ) - (func $assembly/index/__mm_reallocate (; 13 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) - global.get $assembly/index/ROOT + (func $~lib/rt/index/__mm_reallocate (; 14 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + global.get $~lib/rt/tlsf/ROOT i32.eqz if i32.const 0 - i32.const 24 - i32.const 548 + i32.const 72 + i32.const 21 i32.const 13 call $~lib/builtins/abort unreachable @@ -1740,22 +1752,22 @@ i32.eqz if i32.const 0 - i32.const 24 - i32.const 549 + i32.const 72 + i32.const 22 i32.const 2 call $~lib/builtins/abort unreachable end - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 i32.const 16 i32.sub local.get $1 - call $assembly/index/reallocateBlock + call $~lib/rt/tlsf/reallocateBlock i32.const 16 i32.add ) - (func $assembly/index/freeBlock (; 14 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/tlsf/freeBlock (; 15 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) local.get $1 i32.load @@ -1768,7 +1780,7 @@ if i32.const 0 i32.const 24 - i32.const 530 + i32.const 519 i32.const 2 call $~lib/builtins/abort unreachable @@ -1780,15 +1792,15 @@ i32.store local.get $0 local.get $1 - call $assembly/index/insertBlock + call $~lib/rt/tlsf/insertBlock ) - (func $assembly/index/__mm_free (; 15 ;) (type $FUNCSIG$vi) (param $0 i32) - global.get $assembly/index/ROOT + (func $~lib/rt/index/__mm_free (; 16 ;) (type $FUNCSIG$vi) (param $0 i32) + global.get $~lib/rt/tlsf/ROOT i32.eqz if i32.const 0 - i32.const 24 - i32.const 556 + i32.const 72 + i32.const 29 i32.const 13 call $~lib/builtins/abort unreachable @@ -1807,42 +1819,84 @@ i32.eqz if i32.const 0 - i32.const 24 - i32.const 557 + i32.const 72 + i32.const 30 i32.const 2 call $~lib/builtins/abort unreachable end - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 i32.const 16 i32.sub - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock ) - (func $assembly/index/__rt_visit_members (; 16 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/rt/pure/increment (; 17 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + local.get $0 + i32.load offset=4 + local.set $1 + local.get $1 + i32.const 268435455 + i32.const -1 + i32.xor + i32.and + local.get $1 + i32.const 1 + i32.add + i32.const 268435455 + i32.const -1 + i32.xor + i32.and + i32.eq + i32.eqz + if + i32.const 0 + i32.const 120 + i32.const 105 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 1 + i32.add + i32.store offset=4 + ) + (func $~lib/rt/index/__gc_retain (; 18 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $~lib/rt/pure/increment + end + ) + (func $~lib/rt/pure/__rt_visit_members (; 19 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) unreachable ) - (func $assembly/index/__rt_flags (; 17 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/rt/pure/__rt_flags (; 20 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) unreachable ) - (func $~lib/memory/memory.allocate (; 18 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/memory/memory.allocate (; 21 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) i32.const 0 - i32.const 80 + i32.const 168 i32.const 61 i32.const 9 call $~lib/builtins/abort unreachable ) - (func $assembly/index/growRoots (; 19 ;) (type $FUNCSIG$v) + (func $~lib/rt/pure/growRoots (; 22 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) - global.get $assembly/index/ROOTS + global.get $~lib/rt/pure/ROOTS local.set $0 - global.get $assembly/index/CUR + global.get $~lib/rt/pure/CUR local.get $0 i32.sub local.set $1 @@ -1867,26 +1921,26 @@ local.get $1 call $~lib/memory/memory.copy local.get $5 - global.set $assembly/index/ROOTS + global.set $~lib/rt/pure/ROOTS local.get $5 local.get $1 i32.add - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR local.get $5 local.get $4 i32.add - global.set $assembly/index/END + global.set $~lib/rt/pure/END ) - (func $assembly/index/appendRoot (; 20 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/appendRoot (; 23 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) - global.get $assembly/index/CUR + global.get $~lib/rt/pure/CUR local.set $1 local.get $1 - global.get $assembly/index/END + global.get $~lib/rt/pure/END i32.ge_u if - call $assembly/index/growRoots - global.get $assembly/index/CUR + call $~lib/rt/pure/growRoots + global.get $~lib/rt/pure/CUR local.set $1 end local.get $1 @@ -1895,9 +1949,9 @@ local.get $1 i32.const 1 i32.add - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR ) - (func $assembly/index/decrement (; 21 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/decrement (; 24 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) (local $2 i32) local.get $0 @@ -1913,15 +1967,15 @@ if local.get $0 i32.const 1 - call $assembly/index/__rt_visit_members + call $~lib/rt/pure/__rt_visit_members local.get $1 i32.const -2147483648 i32.and i32.eqz if - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock else local.get $0 i32.const -2147483648 @@ -1938,16 +1992,16 @@ i32.eqz if i32.const 0 - i32.const 24 - i32.const 678 + i32.const 120 + i32.const 121 i32.const 15 call $~lib/builtins/abort unreachable end local.get $0 i32.load offset=8 - call $assembly/index/__rt_flags - global.get $assembly/index/ACYCLIC_FLAG + call $~lib/rt/pure/__rt_flags + global.get $~lib/rt/pure/ACYCLIC_FLAG i32.and i32.eqz if @@ -1966,7 +2020,7 @@ i32.eqz if local.get $0 - call $assembly/index/appendRoot + call $~lib/rt/pure/appendRoot end else local.get $0 @@ -1983,7 +2037,16 @@ end end ) - (func $assembly/index/markGray (; 22 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/index/__gc_release (; 25 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $~lib/rt/pure/decrement + end + ) + (func $~lib/rt/pure/markGray (; 26 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -2005,10 +2068,10 @@ i32.store offset=4 local.get $0 i32.const 2 - call $assembly/index/__rt_visit_members + call $~lib/rt/pure/__rt_visit_members end ) - (func $assembly/index/scanBlack (; 23 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/scanBlack (; 27 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 local.get $0 i32.load offset=4 @@ -2021,9 +2084,9 @@ i32.store offset=4 local.get $0 i32.const 4 - call $assembly/index/__rt_visit_members + call $~lib/rt/pure/__rt_visit_members ) - (func $assembly/index/scan (; 24 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/scan (; 28 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -2041,7 +2104,7 @@ i32.gt_u if local.get $0 - call $assembly/index/scanBlack + call $~lib/rt/pure/scanBlack else local.get $0 local.get $1 @@ -2054,11 +2117,11 @@ i32.store offset=4 local.get $0 i32.const 3 - call $assembly/index/__rt_visit_members + call $~lib/rt/pure/__rt_visit_members end end ) - (func $assembly/index/collectWhite (; 25 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/rt/pure/collectWhite (; 29 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -2079,214 +2142,20 @@ if local.get $0 i32.const 5 - call $assembly/index/__rt_visit_members + call $~lib/rt/pure/__rt_visit_members end - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $0 - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock ) - (func $assembly/index/__rt_visit (; 26 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) - (local $2 i32) - block $break|0 - block $case5|0 - block $case4|0 - block $case3|0 - block $case2|0 - block $case1|0 - block $case0|0 - local.get $1 - local.set $2 - local.get $2 - i32.const 1 - i32.eq - br_if $case0|0 - local.get $2 - i32.const 2 - i32.eq - br_if $case1|0 - local.get $2 - i32.const 3 - i32.eq - br_if $case2|0 - local.get $2 - i32.const 4 - i32.eq - br_if $case3|0 - local.get $2 - i32.const 5 - i32.eq - br_if $case4|0 - br $case5|0 - end - block - local.get $0 - call $assembly/index/decrement - br $break|0 - unreachable - end - unreachable - end - block - local.get $0 - i32.load offset=4 - i32.const 268435455 - i32.and - i32.const 0 - i32.gt_u - i32.eqz - if - i32.const 0 - i32.const 24 - i32.const 633 - i32.const 17 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $0 - i32.load offset=4 - i32.const 1 - i32.sub - i32.store offset=4 - local.get $0 - call $assembly/index/markGray - br $break|0 - unreachable - end - unreachable - end - block - local.get $0 - call $assembly/index/scan - br $break|0 - unreachable - end - unreachable - end - block - local.get $0 - i32.load offset=4 - local.set $2 - local.get $2 - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.const 1 - i32.add - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - i32.eq - i32.eqz - if - i32.const 0 - i32.const 24 - i32.const 644 - i32.const 6 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $2 - i32.const 1 - i32.add - i32.store offset=4 - local.get $2 - i32.const 1879048192 - i32.and - i32.const 0 - i32.ne - if - local.get $0 - call $assembly/index/scanBlack - end - br $break|0 - unreachable - end - unreachable - end - block - local.get $0 - call $assembly/index/collectWhite - br $break|0 - unreachable - end - unreachable - end - i32.const 0 - i32.eqz - if - i32.const 0 - i32.const 24 - i32.const 655 - i32.const 24 - call $~lib/builtins/abort - unreachable - end - end - ) - (func $assembly/index/increment (; 27 ;) (type $FUNCSIG$vi) (param $0 i32) - (local $1 i32) - local.get $0 - i32.load offset=4 - local.set $1 - local.get $1 - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - local.get $1 - i32.const 1 - i32.add - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - i32.eq - i32.eqz - if - i32.const 0 - i32.const 24 - i32.const 662 - i32.const 2 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $1 - i32.const 1 - i32.add - i32.store offset=4 - ) - (func $assembly/index/__gc_retain (; 28 ;) (type $FUNCSIG$vi) (param $0 i32) - local.get $0 - if - local.get $0 - i32.const 16 - i32.sub - call $assembly/index/increment - end - ) - (func $assembly/index/__gc_release (; 29 ;) (type $FUNCSIG$vi) (param $0 i32) - local.get $0 - if - local.get $0 - i32.const 16 - i32.sub - call $assembly/index/decrement - end - ) - (func $assembly/index/collectCycles (; 30 ;) (type $FUNCSIG$v) + (func $~lib/rt/pure/collectCycles (; 30 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) - global.get $assembly/index/ROOTS + global.get $~lib/rt/pure/ROOTS local.set $0 local.get $0 local.set $1 @@ -2294,7 +2163,7 @@ block local.get $1 local.set $2 - global.get $assembly/index/CUR + global.get $~lib/rt/pure/CUR local.set $3 end loop $repeat|0 @@ -2326,7 +2195,7 @@ end if local.get $4 - call $assembly/index/markGray + call $~lib/rt/pure/markGray local.get $1 local.get $4 i32.store @@ -2349,9 +2218,9 @@ i32.const 0 end if - global.get $assembly/index/ROOT + global.get $~lib/rt/tlsf/ROOT local.get $4 - call $assembly/index/freeBlock + call $~lib/rt/tlsf/freeBlock else local.get $4 local.get $5 @@ -2373,7 +2242,7 @@ unreachable end local.get $1 - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR block $break|1 local.get $0 local.set $3 @@ -2385,7 +2254,7 @@ br_if $break|1 local.get $3 i32.load - call $assembly/index/scan + call $~lib/rt/pure/scan local.get $3 i32.const 4 i32.add @@ -2417,7 +2286,7 @@ i32.and i32.store offset=4 local.get $2 - call $assembly/index/collectWhite + call $~lib/rt/pure/collectWhite end local.get $3 i32.const 4 @@ -2429,8 +2298,13 @@ unreachable end local.get $0 - global.set $assembly/index/CUR + global.set $~lib/rt/pure/CUR ) - (func $null (; 31 ;) (type $FUNCSIG$v) + (func $~lib/rt/index/__gc_collect (; 31 ;) (type $FUNCSIG$v) + call $~lib/rt/pure/collectCycles + ) + (func $start (; 32 ;) (type $FUNCSIG$v) + ) + (func $null (; 33 ;) (type $FUNCSIG$v) ) )