mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-16 16:31:32 +00:00
unify, stub impl
This commit is contained in:
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
77
std/assembly/rt/stub.ts
Normal 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 {
|
||||
}
|
@ -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;
|
||||
|
Reference in New Issue
Block a user