unify, stub impl

This commit is contained in:
dcode
2019-04-18 12:53:48 +02:00
parent 8216cf3361
commit 18c3f0c555
15 changed files with 346 additions and 141 deletions

View File

@ -4,3 +4,30 @@ 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).
Interface
---------
* **__rt_allocate**(size: `usize`, id: `u32` = 0): `usize`<br />
Dynamically allocates a chunk of memory of at least the specified size and returns its address.
Alignment is guaranteed to be 16 bytes to fit up to v128 values naturally.
* **__rt_free**(ref: `usize`): `void`<br />
Frees a dynamically allocated chunk of memory by its address.
* **__rt_retain**(ref: `usize`): `void`<br />
Retains a reference.
* **__rt_release**(ref: `usize`): `void`<br />
Releases a reference.
* **__rt_collect**(): `void`<br />
Forces a full garbage collection cycle.
* **__rt_typeinfo**(id: `u32`): `void`<br />
Obtains the runtime type information for objects of the kind represented by the specified id.
Stub
----
The fully functional yet minimal [stub implementation](./stub.ts) provides dynamic memory allocation only but doesn't include sophisticated support to deallocate objects. Useful for prototyping or very short-lived programs with hardly any memory footprint.

View File

@ -11,3 +11,29 @@
// @ts-ignore: decorator
@inline export const DEBUG = true;
/** Common block structure. */
@unmanaged export class CommonBlock {
/** Memory manager info. */
mmInfo: usize; // WASM64 might need adaption
/** Garbage collector info. */
gcInfo: u32;
/** Runtime class id. */
rtId: u32;
/** Runtime object size. */
rtSize: u32;
}
/////////////////////////////////// Type information interface ////////////////////////////////////
import { RTTI_BASE } from "../runtime";
import { RTTIData } from "../common/rtti";
// @ts-ignore: decorator
@global @unsafe
function __rt_typeinfo(id: u32): u32 {
var ptr: usize = RTTI_BASE;
return !id || id > load<u32>(ptr)
? unreachable()
: changetype<RTTIData>(ptr + id * offsetof<RTTIData>()).flags;
}

View File

@ -1,52 +1,56 @@
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 /////////////////////////////////////
import { ROOT, Block, BLOCK_OVERHEAD, initializeRoot, allocateBlock, reallocateBlock, freeBlock } from "./tlsf";
// @ts-ignore: decorator
@global @unsafe
function __mm_allocate(size: usize): usize {
function __rt_allocate(size: usize, id: u32): usize {
var root = ROOT;
if (!root) {
initializeRoot();
root = ROOT;
}
return changetype<usize>(allocateBlock(root, size)) + BLOCK_OVERHEAD;
var block = allocateBlock(root, size);
block.rtId = id;
return changetype<usize>(block) + BLOCK_OVERHEAD;
}
// @ts-ignore: decorator
@global @unsafe
function __mm_reallocate(data: usize, size: usize): usize {
function __rt_reallocate(ref: usize, size: usize): usize {
if (DEBUG) assert(ROOT); // must be initialized
assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned
return changetype<usize>(reallocateBlock(ROOT, changetype<Block>(data - BLOCK_OVERHEAD), size)) + BLOCK_OVERHEAD;
assert(ref != 0 && !(ref & AL_MASK)); // must exist and be aligned
return changetype<usize>(reallocateBlock(ROOT, changetype<Block>(ref - BLOCK_OVERHEAD), size)) + BLOCK_OVERHEAD;
}
// @ts-ignore: decorator
@global @unsafe
function __mm_free(data: usize): void {
function __rt_free(ref: usize): void {
if (DEBUG) assert(ROOT); // must be initialized
assert(data != 0 && !(data & AL_MASK)); // must exist and be aligned
freeBlock(ROOT, changetype<Block>(data - BLOCK_OVERHEAD));
assert(ref != 0 && !(ref & AL_MASK)); // must exist and be aligned
freeBlock(ROOT, changetype<Block>(ref - BLOCK_OVERHEAD));
}
/////////////////////////////////// Garbage collector interface ///////////////////////////////////
import { increment, decrement, collectCycles } from "./pure";
// @ts-ignore: decorator
@global @unsafe
function __gc_retain(ref: usize): void {
function __rt_retain(ref: usize): void {
if (ref) increment(changetype<Block>(ref - BLOCK_OVERHEAD));
}
// @ts-ignore: decorator
@global @unsafe
function __gc_release(ref: usize): void {
function __rt_release(ref: usize): void {
if (ref) decrement(changetype<Block>(ref - BLOCK_OVERHEAD));
}
// @ts-ignore: decorator
@global @unsafe
function __gc_collect(): void {
function __rt_collect(): void {
collectCycles();
}

77
std/assembly/rt/stub.ts Normal file
View File

@ -0,0 +1,77 @@
import { AL_MASK, CommonBlock } from "./common";
// @ts-ignore: decorator
@inline
const BLOCK_OVERHEAD = offsetof<CommonBlock>();
// @ts-ignore: decorator
@inline
const BLOCK_MAXSIZE: usize = (1 << 30) - BLOCK_OVERHEAD; // match TLSF
// @ts-ignore: decorator
@lazy
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
// @ts-ignore: decorator
@lazy
var offset: usize = startOffset;
//////////////////////////////////// Memory manager interface /////////////////////////////////////
// @ts-ignore: decorator
@unsafe @global
function __rt_allocate(size: usize, id: u32): usize {
if (size > BLOCK_MAXSIZE) unreachable();
var ptr = offset + BLOCK_OVERHEAD;
var newPtr = (ptr + max<usize>(size, 1) + AL_MASK) & ~AL_MASK;
var pagesBefore = memory.size();
if (newPtr > <usize>pagesBefore << 16) {
let pagesNeeded = ((newPtr - ptr + 0xffff) & ~0xffff) >>> 16;
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
if (memory.grow(pagesWanted) < 0) {
if (memory.grow(pagesNeeded) < 0) unreachable(); // out of memory
}
}
offset = newPtr;
var block = changetype<CommonBlock>(ptr - BLOCK_OVERHEAD);
block.rtId = id;
block.rtSize = size;
return ptr;
}
// @ts-ignore: decorator
@unsafe @global
function __rt_reallocate(ref: usize, size: usize): usize {
var block = changetype<CommonBlock>(ref - BLOCK_OVERHEAD);
var newRef = __rt_allocate(size, block.rtId);
memory.copy(newRef, ref, block.rtSize);
return newRef;
}
// @ts-ignore: decorator
@unsafe @global
function __rt_free(ref: usize): void {
}
// @ts-ignore: decorator
@unsafe @global
function __rt_reset(): void { // special
offset = startOffset;
}
/////////////////////////////////// Garbage collector interface ///////////////////////////////////
// @ts-ignore: decorator
@global @unsafe
function __rt_retain(ref: usize): void {
}
// @ts-ignore: decorator
@global @unsafe
function __rt_release(ref: usize): void {
}
// @ts-ignore: decorator
@global @unsafe
function __rt_collect(): void {
}

View File

@ -1,4 +1,4 @@
import { AL_BITS, AL_SIZE, AL_MASK, DEBUG } from "./common";
import { AL_BITS, AL_SIZE, AL_MASK, DEBUG, CommonBlock } from "./common";
/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator ///////////////////////
// see: http://www.gii.upv.es/tlsf/
@ -69,16 +69,7 @@ import { AL_BITS, AL_SIZE, AL_MASK, DEBUG } from "./common";
// │ 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;
@unmanaged export class Block extends CommonBlock {
/** Previous free block, if any. Only valid if free, otherwise part of payload. */
prev: Block | null;