Initial GC integration (#196)

This commit is contained in:
Daniel Wirtz
2018-08-02 18:23:02 +02:00
committed by GitHub
parent 671121bf70
commit dc0f271fc2
139 changed files with 7370 additions and 5016 deletions

View File

@ -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);
}