mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-30 17:32:16 +00:00
rt docs, initial itcm wiring
This commit is contained in:
parent
3fc9f550ad
commit
d4d5814fc2
@ -4367,17 +4367,20 @@ export function ensureGCHook(
|
||||
// check if the class implements a custom GC function (only valid for library elements)
|
||||
var members = classInstance.members;
|
||||
if (classInstance.isDeclaredInLibrary) {
|
||||
if (members !== null && members.has("__gc")) {
|
||||
let gcPrototype = assert(members.get("__gc"));
|
||||
assert(gcPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>gcPrototype, null));
|
||||
assert(gcInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(!gcInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
assert(gcInstance.signature.parameterTypes.length == 0);
|
||||
assert(gcInstance.signature.returnType == Type.void);
|
||||
gcInstance.internalName = classInstance.internalName + "~gc";
|
||||
assert(compiler.compileFunction(gcInstance));
|
||||
let index = compiler.ensureFunctionTableEntry(gcInstance);
|
||||
if (members !== null && members.has("__iter")) {
|
||||
let iterPrototype = assert(members.get("__iter"));
|
||||
assert(iterPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let iterInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>iterPrototype, null));
|
||||
assert(iterInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(!iterInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
let signature = iterInstance.signature;
|
||||
let parameterTypes = signature.parameterTypes;
|
||||
assert(parameterTypes.length == 1);
|
||||
assert(parameterTypes[0].signatureReference);
|
||||
assert(signature.returnType == Type.void);
|
||||
iterInstance.internalName = classInstance.internalName + "~iter";
|
||||
assert(compiler.compileFunction(iterInstance));
|
||||
let index = compiler.ensureFunctionTableEntry(iterInstance);
|
||||
classInstance.gcHookIndex = index;
|
||||
return index;
|
||||
}
|
||||
@ -4408,7 +4411,7 @@ export function ensureGCHook(
|
||||
functionTable.push("<placeholder>");
|
||||
classInstance.gcHookIndex = gcHookIndex;
|
||||
|
||||
// if the class extends a base class, call its hook first (calls mark)
|
||||
// if the class extends a base class, call its hook first
|
||||
var baseInstance = classInstance.base;
|
||||
if (baseInstance) {
|
||||
assert(baseInstance.type.isManaged(program));
|
||||
@ -4418,19 +4421,12 @@ export function ensureGCHook(
|
||||
ensureGCHook(compiler, <Class>baseInstance.type.classReference)
|
||||
),
|
||||
[
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
module.createGetLocal(0, nativeSizeType), // this
|
||||
module.createGetLocal(1, NativeType.I32) // fn
|
||||
],
|
||||
"FUNCSIG$" + (nativeSizeType == NativeType.I64 ? "vj" : "vi")
|
||||
"FUNCSIG$" + (nativeSizeType == NativeType.I64 ? "vji" : "vii")
|
||||
)
|
||||
);
|
||||
|
||||
// if this class is the top-most base class, mark the instance
|
||||
} else {
|
||||
body.push(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
|
||||
// mark instances assigned to own fields that are again references
|
||||
@ -4442,16 +4438,20 @@ export function ensureGCHook(
|
||||
if (type.isManaged(program)) {
|
||||
let offset = (<Field>member).memoryOffset;
|
||||
assert(offset >= 0);
|
||||
body.push(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
offset
|
||||
)
|
||||
], NativeType.None)
|
||||
body.push( // fn(fieldValue)
|
||||
module.createCallIndirect(
|
||||
module.createGetLocal(1, NativeType.I32),
|
||||
[
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
offset
|
||||
),
|
||||
],
|
||||
"FUNCSIG$vi"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4460,7 +4460,7 @@ export function ensureGCHook(
|
||||
}
|
||||
|
||||
// add the function to the module and return its table index
|
||||
var funcName = classInstance.internalName + "~gc";
|
||||
var funcName = classInstance.internalName + "~iter";
|
||||
module.addFunction(
|
||||
funcName,
|
||||
compiler.ensureFunctionType(null, Type.void, options.usizeType),
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
compileCall as compileBuiltinCall,
|
||||
compileAbort,
|
||||
compileIterateRoots,
|
||||
ensureGCHook,
|
||||
BuiltinSymbols,
|
||||
compileBuiltinArrayGet,
|
||||
compileBuiltinArraySet,
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user