Make sure all roots are iterated by delaying builtin generation; Cleanup

This commit is contained in:
dcodeIO
2018-07-20 20:32:25 +02:00
parent 41ad2f8a70
commit 2fa7d6678c
11 changed files with 411 additions and 871 deletions

View File

@ -6,13 +6,17 @@
// Largely based on the Bach Le's μgc, see: https://github.com/bullno1/ugc
const TRACE = true;
const TRACE = false;
import {
AL_MASK,
MAX_SIZE_32
} from "../internal/allocator";
import {
iterateRoots
} from "../gc";
/** Collector states. */
const enum State {
/** Not yet initialized. */
@ -31,8 +35,8 @@ var state = State.INIT;
var white = 0;
// From and to spaces
var from: ManagedObject;
var to: ManagedObject;
var from: ManagedObjectSet;
var to: ManagedObjectSet;
var iter: ManagedObject;
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
@ -83,16 +87,6 @@ class ManagedObject {
this.nextWithColor = (this.nextWithColor & ~3) | color;
}
/** Inserts an object to this list. */
push(obj: ManagedObject): void {
var prev = this.prev;
trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
obj.next = this;
obj.prev = prev;
prev.next = obj;
this.prev = obj;
}
/** Unlinks this object from its list. */
unlink(): void {
var next = this.next;
@ -102,12 +96,6 @@ class ManagedObject {
prev.next = next;
}
clear(): void {
if (TRACE) trace(" clear", 1, objToRef(this));
this.nextWithColor = changetype<usize>(this);
this.prev = this;
}
/** Marks this object as gray, that is reachable with unscanned children. */
makeGray(): void {
if (TRACE) trace(" makeGray", 1, objToRef(this));
@ -119,24 +107,38 @@ class ManagedObject {
}
}
function markRoots(): void {
if (TRACE) trace(" markRoots");
gc.iterateRoots(function markRoot(ref: usize): void {
if (TRACE) trace(" markRoot", 1, ref);
if (ref) __gc_mark(ref);
});
/** A set of managed objects. Used for the from and to spaces. */
@unmanaged
class ManagedObjectSet extends ManagedObject {
/** Inserts an object. */
push(obj: ManagedObject): void {
var prev = this.prev;
if (TRACE) trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
obj.next = this;
obj.prev = prev;
prev.next = obj;
this.prev = obj;
}
/** Clears this list. */
clear(): void {
if (TRACE) trace(" clear", 1, objToRef(this));
this.nextWithColor = changetype<usize>(this);
this.prev = this;
}
}
/** Performs a single step according to the current state. */
function step(): void {
export function step(): bool {
var obj: ManagedObject;
switch (state) {
case State.INIT: {
if (TRACE) trace("gc~step/INIT");
from = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
from = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
from.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
from.clear();
to = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE));
to = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
to.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
to.clear();
iter = to;
@ -146,7 +148,7 @@ function step(): void {
}
case State.IDLE: {
if (TRACE) trace("gc~step/IDLE");
markRoots();
iterateRoots(__gc_mark);
state = State.MARK;
if (TRACE) trace("gc~state = MARK");
break;
@ -160,7 +162,7 @@ function step(): void {
obj.visitFn(objToRef(obj));
} else {
if (TRACE) trace("gc~step/MARK finish");
markRoots();
iterateRoots(__gc_mark);
obj = iter.next;
if (obj === to) {
let prevFrom = from;
@ -189,6 +191,7 @@ function step(): void {
break;
}
}
return state != State.IDLE;
}
@inline function refToObj(ref: usize): ManagedObject {
@ -201,7 +204,6 @@ function step(): void {
// Garbage collector interface
/** Allocates a managed object. */
@global export function __gc_allocate(
size: usize,
visitFn: (ref: usize) => void
@ -216,21 +218,20 @@ function step(): void {
return objToRef(obj);
}
/** Marks a reachable object. Called from the visitFn functions. */
@global export function __gc_mark(ref: usize): void {
if (TRACE) trace("gc.mark", 1, ref);
var obj = refToObj(ref);
if (obj.color == white) obj.makeGray();
}
/** Links a managed child object to its parent object. */
@global export function __gc_link(parentRef: usize, childRef: usize): void {
if (TRACE) trace("gc.link", 2, parentRef, childRef);
var parent = refToObj(parentRef);
if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray();
}
/** Performs a full garbage collection cycle. */
@global 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();
}
}
@global export function __gc_collect(): void {
if (TRACE) trace("gc.collect");
// begin collecting if not yet collecting

View File

@ -1,6 +1,6 @@
export namespace gc {
@builtin export declare function iterateRoots(fn: (ref: usize) => void): void; // tslint:disable-line
@builtin export declare function iterateRoots(fn: (ref: usize) => void): void; // tslint:disable-line
export namespace gc {
export function allocate(size: usize, visitFn: (ref: usize) => void): usize {
if (isDefined(__gc_allocate)) return __gc_allocate(size, visitFn); // tslint:disable-line
@ -8,9 +8,9 @@ export namespace gc {
return <usize>unreachable();
}
export function mark(ref: usize): void {
if (isDefined(__gc_mark)) { __gc_mark(ref); return; } // tslint:disable-line
WARNING("Calling 'gc.mark' requires a garbage collector to be present.");
export function collect(): void {
if (isDefined(__gc_collect)) { __gc_collect(); return; } // tslint:disable-line
WARNING("Calling 'gc.collect' requires a garbage collector to be present.");
unreachable();
}
@ -20,9 +20,9 @@ export namespace gc {
unreachable();
}
export function collect(): void {
if (isDefined(__gc_collect)) { __gc_collect(); return; } // tslint:disable-line
WARNING("Calling 'gc.collect' requires a garbage collector to be present.");
export function mark(ref: usize): void {
if (isDefined(__gc_mark)) { __gc_mark(ref); return; } // tslint:disable-line
WARNING("Calling 'gc.mark' requires a garbage collector to be present.");
unreachable();
}
}

View File

@ -350,16 +350,14 @@ declare namespace memory {
/** Garbage collector operations. */
declare namespace gc {
/** Calls the specified function with every reference within the root set. */
export function iterateRoots(fn: (ref: usize) => void): void;
/** Allocates a managed object identified by its visitor function. */
export function allocate(size: usize, visitFn: (ref: usize) => void): usize;
/** Marks a managed object as reachable. */
export function mark(ref: usize): void;
/** Links a managed child with its parent. */
export function link(parentRef: usize, childRef: usize): void;
/** Performs a full garbage collection cycle. */
export function collect(): void;
/** Must be called when a managed object becomes a child of another one. */
export function link(parentRef: usize, childRef: usize): void;
/** Must be called when a managed object is found reachable. */
export function mark(ref: usize): void;
}
/** Table operations. */