mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-23 11:41:45 +00:00
Initial GC integration (#196)
This commit is contained in:
@ -4,18 +4,15 @@
|
||||
* @module std/assembly/collector/itcm
|
||||
*//***/
|
||||
|
||||
// Largely based on the Bach Le's μgc, see: https://github.com/bullno1/ugc
|
||||
// Largely based on Bach Le's μgc, see: https://github.com/bullno1/ugc
|
||||
|
||||
const TRACE = false;
|
||||
|
||||
import {
|
||||
AL_MASK,
|
||||
MAX_SIZE_32
|
||||
} from "../internal/allocator";
|
||||
/** Size of a managed object header. */
|
||||
export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
|
||||
|
||||
import {
|
||||
iterateRoots
|
||||
} from "../gc";
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
|
||||
import { iterateRoots } from "../gc";
|
||||
|
||||
/** Collector states. */
|
||||
const enum State {
|
||||
@ -35,8 +32,8 @@ var state = State.INIT;
|
||||
var white = 0;
|
||||
|
||||
// From and to spaces
|
||||
var from: ManagedObjectList;
|
||||
var to: ManagedObjectList;
|
||||
var fromSpace: ManagedObjectList;
|
||||
var toSpace: ManagedObjectList;
|
||||
var iter: ManagedObject;
|
||||
|
||||
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
|
||||
@ -47,15 +44,14 @@ var iter: ManagedObject;
|
||||
// ├─────────────────────────────────────────────────────────┴─┴───┤ │ usize
|
||||
// │ prev │ ◄─┘
|
||||
// ├───────────────────────────────────────────────────────────────┤
|
||||
// │ visitFn │
|
||||
// │ hookFn │
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ SIZE ┘ ◄─ user-space reference
|
||||
// │ ... data ... │
|
||||
// └───────────────────────────────────────────────────────────────┘
|
||||
// C: color
|
||||
|
||||
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
|
||||
@unmanaged
|
||||
class ManagedObject {
|
||||
@unmanaged class ManagedObject {
|
||||
|
||||
/** Pointer to the next object with color flags stored in the alignment bits. */
|
||||
nextWithColor: usize;
|
||||
@ -63,11 +59,8 @@ class ManagedObject {
|
||||
/** Pointer to the previous object. */
|
||||
prev: ManagedObject;
|
||||
|
||||
/** Visitor function called with the user-space reference. */
|
||||
visitFn: (ref: usize) => void;
|
||||
|
||||
/** Size of a managed object after alignment. */
|
||||
static readonly SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
|
||||
/** Class-specific hook function called with the user-space reference. */
|
||||
hookFn: (ref: usize) => void;
|
||||
|
||||
/** Gets the pointer to the next object. */
|
||||
get next(): ManagedObject {
|
||||
@ -104,14 +97,13 @@ class ManagedObject {
|
||||
const gray = 2;
|
||||
if (this == iter) iter = this.prev;
|
||||
this.unlink();
|
||||
to.push(this);
|
||||
toSpace.push(this);
|
||||
this.nextWithColor = (this.nextWithColor & ~3) | gray;
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of managed objects. Used for the from and to spaces. */
|
||||
@unmanaged
|
||||
class ManagedObjectList extends ManagedObject {
|
||||
@unmanaged class ManagedObjectList extends ManagedObject {
|
||||
|
||||
/** Inserts an object. */
|
||||
push(obj: ManagedObject): void {
|
||||
@ -137,13 +129,13 @@ function step(): void {
|
||||
switch (state) {
|
||||
case State.INIT: {
|
||||
if (TRACE) trace("gc~step/INIT");
|
||||
from = changetype<ManagedObjectList>(memory.allocate(ManagedObject.SIZE));
|
||||
from.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
from.clear();
|
||||
to = changetype<ManagedObjectList>(memory.allocate(ManagedObject.SIZE));
|
||||
to.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
to.clear();
|
||||
iter = to;
|
||||
fromSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
|
||||
fromSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
fromSpace.clear();
|
||||
toSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
|
||||
toSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
toSpace.clear();
|
||||
iter = toSpace;
|
||||
state = State.IDLE;
|
||||
if (TRACE) trace("gc~state = IDLE");
|
||||
// fall-through
|
||||
@ -157,21 +149,21 @@ function step(): void {
|
||||
}
|
||||
case State.MARK: {
|
||||
obj = iter.next;
|
||||
if (obj !== to) {
|
||||
if (obj !== toSpace) {
|
||||
if (TRACE) trace("gc~step/MARK iterate", 1, objToRef(obj));
|
||||
iter = obj;
|
||||
obj.color = <i32>!white;
|
||||
obj.visitFn(objToRef(obj));
|
||||
obj.hookFn(objToRef(obj));
|
||||
} else {
|
||||
if (TRACE) trace("gc~step/MARK finish");
|
||||
iterateRoots(__gc_mark);
|
||||
obj = iter.next;
|
||||
if (obj === to) {
|
||||
let prevFrom = from;
|
||||
from = to;
|
||||
to = prevFrom;
|
||||
if (obj === toSpace) {
|
||||
let from = fromSpace;
|
||||
fromSpace = toSpace;
|
||||
toSpace = from;
|
||||
white = <i32>!white;
|
||||
iter = prevFrom.next;
|
||||
iter = from.next;
|
||||
state = State.SWEEP;
|
||||
if (TRACE) trace("gc~state = SWEEP");
|
||||
}
|
||||
@ -180,13 +172,13 @@ function step(): void {
|
||||
}
|
||||
case State.SWEEP: {
|
||||
obj = iter;
|
||||
if (obj !== to) {
|
||||
if (obj !== toSpace) {
|
||||
if (TRACE) trace("gc~step/SWEEP free", 1, objToRef(obj));
|
||||
iter = obj.next;
|
||||
memory.free(changetype<usize>(obj));
|
||||
if (changetype<usize>(obj) >= HEAP_BASE) memory.free(changetype<usize>(obj));
|
||||
} else {
|
||||
if (TRACE) trace("gc~step/SWEEP finish");
|
||||
to.clear();
|
||||
toSpace.clear();
|
||||
state = State.IDLE;
|
||||
if (TRACE) trace("gc~state = IDLE");
|
||||
}
|
||||
@ -196,26 +188,26 @@ function step(): void {
|
||||
}
|
||||
|
||||
@inline function refToObj(ref: usize): ManagedObject {
|
||||
return changetype<ManagedObject>(ref - ManagedObject.SIZE);
|
||||
return changetype<ManagedObject>(ref - HEADER_SIZE);
|
||||
}
|
||||
|
||||
@inline function objToRef(obj: ManagedObject): usize {
|
||||
return changetype<usize>(obj) + ManagedObject.SIZE;
|
||||
return changetype<usize>(obj) + HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Garbage collector interface
|
||||
|
||||
@global export function __gc_allocate(
|
||||
size: usize,
|
||||
visitFn: (ref: usize) => void
|
||||
markFn: (ref: usize) => void
|
||||
): usize {
|
||||
if (TRACE) trace("gc.allocate", 1, size);
|
||||
if (size > MAX_SIZE_32 - ManagedObject.SIZE) unreachable();
|
||||
if (size > MAX_SIZE_32 - HEADER_SIZE) unreachable();
|
||||
step(); // also makes sure it's initialized
|
||||
var obj = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE + size));
|
||||
obj.visitFn = visitFn;
|
||||
var obj = changetype<ManagedObject>(memory.allocate(HEADER_SIZE + size));
|
||||
obj.hookFn = markFn;
|
||||
obj.color = white;
|
||||
from.push(obj);
|
||||
fromSpace.push(obj);
|
||||
return objToRef(obj);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user