rt docs, initial itcm wiring

This commit is contained in:
dcode
2019-03-21 12:29:49 +01:00
parent 3fc9f550ad
commit d4d5814fc2
4 changed files with 124 additions and 66 deletions

View File

@ -4,13 +4,7 @@
@inline
const TRACE = false;
/** Size of a managed object header. */
// @ts-ignore: decorator
@inline
export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
import { ITERATEROOTS } from "../runtime";
import { AL_MASK, MAX_SIZE_32 } from "../util/allocator";
import { ITERATEROOTS, HEADER_SIZE } from "../runtime";
/** Collector states. */
const enum State {
@ -51,6 +45,11 @@ var iter: ManagedObject;
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
@unmanaged class ManagedObject {
// <HEADER>
classId: u32;
payloadSize: u32;
// </HEADER>
/** Pointer to the next object with color flags stored in the alignment bits. */
nextWithColor: usize;
@ -58,7 +57,9 @@ var iter: ManagedObject;
prev: ManagedObject;
/** Class-specific hook function called with the user-space reference. */
hookFn: (ref: usize) => void;
get hookFn(): (ref: usize) => void {
return changetype<(ref: usize) => void>(this.classId);
}
/** Gets the pointer to the next object. */
get next(): ManagedObject {
@ -128,10 +129,10 @@ function step(): void {
case State.INIT: {
if (TRACE) trace("gc~step/INIT");
fromSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
fromSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
fromSpace.classId = -1; // would error
fromSpace.clear();
toSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
toSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
toSpace.classId = -1; // would error
toSpace.clear();
iter = toSpace;
state = State.IDLE;
@ -140,7 +141,10 @@ function step(): void {
}
case State.IDLE: {
if (TRACE) trace("gc~step/IDLE");
ITERATEROOTS(__gc_mark);
ITERATEROOTS((ref: usize): void => {
var obj = refToObj(ref);
if (obj.color == white) obj.makeGray();
});
state = State.MARK;
if (TRACE) trace("gc~state = MARK");
break;
@ -161,7 +165,10 @@ function step(): void {
obj.hookFn(objToRef(obj));
} else {
if (TRACE) trace("gc~step/MARK finish");
ITERATEROOTS(__gc_mark);
ITERATEROOTS((ref: usize): void => {
var obj = refToObj(ref);
if (obj.color == white) obj.makeGray();
});
obj = iter.next;
if (obj === toSpace) {
let from = fromSpace;
@ -206,36 +213,26 @@ function objToRef(obj: ManagedObject): usize {
// @ts-ignore: decorator
@global @unsafe
export function __gc_allocate( // TODO: make this register only / reuse header
size: usize,
markFn: (ref: usize) => void
): usize {
if (TRACE) trace("gc.allocate", 1, size);
if (size > MAX_SIZE_32 - HEADER_SIZE) unreachable();
export function __gc_register(ref: usize): void {
if (TRACE) trace("gc.register", 2, ref);
step(); // also makes sure it's initialized
var obj = changetype<ManagedObject>(memory.allocate(HEADER_SIZE + size));
obj.hookFn = markFn;
var obj = refToObj(ref);
obj.color = white;
fromSpace.push(obj);
return objToRef(obj);
}
// @ts-ignore: decorator
@global @unsafe
export function __gc_link(parentRef: usize, childRef: usize): void {
if (TRACE) trace("gc.link", 2, parentRef, childRef);
export function __gc_retain(ref: usize, parentRef: usize): void {
if (TRACE) trace("gc.retain", 2, ref, parentRef);
var parent = refToObj(parentRef);
if (parent.color == i32(!white) && refToObj(childRef).color == white) parent.makeGray();
if (parent.color == i32(!white) && refToObj(ref).color == white) parent.makeGray();
}
// @ts-ignore: decorator
@global @unsafe
export function __gc_mark(ref: usize): void {
if (TRACE) trace("gc.mark", 1, ref);
if (ref) {
let obj = refToObj(ref);
if (obj.color == white) obj.makeGray();
}
export function __gc_release(ref: usize, parentRef: usize): void {
if (TRACE) trace("gc.release", 2, ref, parentRef);
}
// @ts-ignore: decorator

View File

@ -1,6 +1,68 @@
// The runtime provides a set of macros for dealing with common AssemblyScript internals, like
// allocation, memory management in general, integration with a (potenial) garbage collector
// and interfaces to hard-wired data types like buffers and their views. Doing so ensures that
// no matter which underlying implementation of a memory allocator or garbage collector is used,
// as long as all runtime/managed objects adhere to the runtime conventions, it'll all play well
// together. The compiler assumes that it can itself use the macros with the signatures declared
// in this file, so changing anything here will most likely require changes to the compiler, too.
import { AL_MASK, MAX_SIZE_32 } from "./util/allocator";
import { HEAP_BASE, memory } from "./memory";
// ALLOCATE(size)
// --------------
// Allocates a runtime object that might eventually make its way into GC'ed userland as a
// managed object. Implicitly prepends the common runtime header to the allocation.
//
// REALLOCATE(ref, size)
// ---------------------
// Changes the size of a previously allocated, but not yet registered, runtime object, for
// example when a pre-allocated buffer turned out to be too small or too large. This works by
// aligning dynamic allocations to actual block size internally so in the best case REALLOCATE
// only changes a size while in the worst case moves the object to larger block.
//
// DISCARD(ref)
// ------------
// Discards a runtime object that has not been registed and turned out to be unnecessary.
// Essentially undoes the forgoing ALLOCATE. Should be avoided where possible, of course.
//
// REGISTER<T>(ref)
// ----------------
// Registers a runtime object of kind T. Sets the internal class id within the runtime header
// and asserts that the object hasn't been registered yet. If a tracing garbage collector is
// present that requires initial insertion, the macro also forwards a call to it. Once a
// runtime object has been registed (makes it into userland), it cannot be DISCARD'ed anymore.
//
// RETAIN<T,TParent>(ref, parentRef)
// ---------------------------------
// Introduces a new reference to ref hold by parentRef. A tracing garbage collector will most
// likely link the runtime object within its internal graph when RETAIN is called, while a
// reference counting collector will increment the reference count.
//
// RELEASE<T,TParent>(ref, parentRef)
// ----------------------------------
// Releases a reference to ref hold by parentRef. A tracing garbage collector will most likely
// ignore this by design, while a reference counting collector decrements the reference count
// and potentially frees the runtime object.
//
// ALLOCATE_UNMANAGED(size)
// ------------------------
// Allocates an unmanaged struct-like object. This is used by the compiler as an abstraction
// to memory.allocate just in case, and is usually not used directly.
//
// WRAPARRAY<T>(buffer)
// --------------------
// Wraps a buffer's data as a standard array of element type T. Used by the compiler when
// creating an array from a static data segment, but is usually not used directly.
//
// HEADER
// ------
// The common runtime object header prepended to all managed objects. Has a size of 16 bytes in
// WASM32 and contains a classId (e.g. for instanceof checks), the allocation size (e.g. for
// .byteLength and .length computation) and additional reserved fields to be used by GC. If no
// GC is present, the HEADER is cut into half excluding the reserved fields, as indicated by
// HEADER_SIZE.
/** Whether the memory manager interface is implemented. */
// @ts-ignore: decorator, stub
@lazy export const MM_IMPLEMENTED: bool = isDefined(__memory_allocate);
@ -157,7 +219,7 @@ function doRetain(ref: usize, parentRef: usize): void {
assertRegistered(parentRef);
}
// @ts-ignore: stub
if (GC_IMPLEMENTED) __gc_link(changetype<usize>(ref), changetype<usize>(parentRef));
if (GC_IMPLEMENTED) __gc_retain(changetype<usize>(ref), changetype<usize>(parentRef));
}
/** Releases a registered object. */
@ -175,7 +237,7 @@ function doRelease(ref: usize, parentRef: usize): void {
assertRegistered(parentRef);
}
// @ts-ignore: stub
if (GC_IMPLEMENTED) __gc_unlink(changetype<usize>(ref), changetype<usize>(parentRef));
if (GC_IMPLEMENTED) __gc_release(changetype<usize>(ref), changetype<usize>(parentRef));
}
/** Discards an unregistered object that turned out to be unnecessary. */