Make an interface around gc.* fwiw

This commit is contained in:
dcodeIO 2018-07-19 16:15:56 +02:00
parent 34839353fd
commit fafaf423b4
13 changed files with 183 additions and 98 deletions

View File

@ -12,6 +12,8 @@ import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
var offset: usize = startOffset;
// Memory allocator interface
@global
export function __memory_allocate(size: usize): usize {
if (size) {
@ -35,9 +37,7 @@ export function __memory_allocate(size: usize): usize {
}
@global
export function __memory_free(ptr: usize): void {
// nop
}
export function __memory_free(ptr: usize): void { /* nop */ }
@global
export function __memory_reset(): void {

View File

@ -338,8 +338,9 @@ function lower_bucket_limit(bucket: usize): u32 {
return 1;
}
@global
export function __memory_allocate(request: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(request: usize): usize {
var original_bucket: usize, bucket: usize;
/*
@ -473,8 +474,7 @@ export function __memory_allocate(request: usize): usize {
return 0;
}
@global
export function __memory_free(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
var bucket: usize, i: usize;
/*
@ -538,8 +538,3 @@ export function __memory_free(ptr: usize): void {
*/
list_push(buckets$get(bucket), changetype<List>(ptr_for_node(i, bucket)));
}
@global
export function __memory_reset(): void {
unreachable();
}

View File

@ -11,17 +11,12 @@
declare function _malloc(size: usize): usize;
declare function _free(ptr: usize): void;
@global
export function __memory_allocate(size: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(size: usize): usize {
return _malloc(size);
}
@global
export function __memory_free(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
_free(ptr);
}
@global
export function __memory_reset(): void {
unreachable();
}

View File

@ -10,17 +10,12 @@
declare function malloc(size: usize): usize;
declare function free(ptr: usize): void;
@global
export function __memory_allocate(size: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(size: usize): usize {
return malloc(size);
}
@global
export function __memory_free(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
free(ptr);
}
@global
export function __memory_reset(): void {
unreachable();
}

View File

@ -433,11 +433,10 @@ function fls<T>(word: T): T {
/** Reference to the initialized {@link Root} structure, once initialized. */
var ROOT: Root = changetype<Root>(0);
// External interface
// Memory allocator interface
/** Allocates a chunk of memory. */
@global
export function __memory_allocate(size: usize): usize {
@global export function __memory_allocate(size: usize): usize {
// initialize if necessary
var root = ROOT;
@ -490,8 +489,7 @@ export function __memory_allocate(size: usize): usize {
}
/** Frees the chunk of memory at the specified address. */
@global
export function __memory_free(data: usize): void {
@global export function __memory_free(data: usize): void {
if (data) {
let root = ROOT;
if (root) {
@ -504,7 +502,6 @@ export function __memory_free(data: usize): void {
}
}
@global
export function __memory_reset(): void {
@global export function __memory_reset(): void {
unreachable();
}

View File

@ -11,10 +11,6 @@ import {
MAX_SIZE_32
} from "../internal/allocator";
import {
__gc_iterate_roots
} from "../builtins";
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
// 3 2 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
@ -145,7 +141,7 @@ var set2: ManagedObject;
var iter: ManagedObject;
/** Performs a single step according to the current state. */
function gc_step(): void {
function step(): void {
var obj: ManagedObject;
switch (state) {
case State.INIT: {
@ -158,7 +154,7 @@ function gc_step(): void {
}
case State.IDLE: {
// start by marking roots
__gc_iterate_roots(function mark_root(ref: usize): void {
gc.iterateRoots(function mark_root(ref: usize): void {
if (ref) {
let obj = changetype<ManagedObject>(ref - ManagedObject.SIZE);
obj.makeBlack();
@ -202,42 +198,50 @@ function gc_step(): void {
}
}
/** Garbage collector interface. */
@global
export namespace gc {
/** Allocates a managed object. */
export function alloc(
size: usize,
visitFn: (ref: usize) => void
): usize {
assert(size <= MAX_SIZE_32 - ManagedObject.SIZE);
var obj = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE + size));
obj.makeWhite();
obj.visitFn = visitFn;
set1.insert(obj);
return changetype<usize>(obj) + ManagedObject.SIZE;
}
/** Visits a reachable object. Called from the visitFn functions. */
export function visit(obj: ManagedObject): void {
if (state == State.SWEEP) return;
if (obj.isWhite) obj.makeGray();
}
/** References a managed child object from its parent object. */
export function ref(parent: ManagedObject, child: ManagedObject): void {
if (parent.isBlack && child.isWhite) parent.makeGray();
}
/** Performs a full garbage collection cycle. */
export function collect(): void {
// begin collecting if not yet collecting
switch (state) {
case State.INIT:
case State.IDLE: gc_step();
}
// finish the cycle
while (state != State.IDLE) gc_step();
}
@inline function refToObj(ref: usize): ManagedObject {
return changetype<ManagedObject>(ref - ManagedObject.SIZE);
}
@inline function objToRef(obj: ManagedObject): usize {
return changetype<usize>(obj) + ManagedObject.SIZE;
}
// Garbage collector interface
/** Allocates a managed object. */
@global export function __gc_allocate(
size: usize,
visitFn: (ref: usize) => void
): usize {
assert(size <= MAX_SIZE_32 - ManagedObject.SIZE);
var obj = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE + size));
obj.makeWhite();
obj.visitFn = visitFn;
set1.insert(obj);
return objToRef(obj);
}
/** Marks a reachable object. Called from the visitFn functions. */
@global export function __gc_mark(ref: usize): void {
var obj = refToObj(ref);
if (state == State.SWEEP) return;
if (obj.isWhite) obj.makeGray();
}
/** Links a managed child object to its parent object. */
@global export function __gc_link(parentRef: usize, childRef: usize): void {
var parent = refToObj(parentRef);
var child = refToObj(childRef);
if (parent.isBlack && child.isWhite) parent.makeGray();
}
/** Performs a full garbage collection cycle. */
@global export function __gc_collect(): void {
// begin collecting if not yet collecting
switch (state) {
case State.INIT:
case State.IDLE: step();
}
// finish the cycle
while (state != State.IDLE) step();
}

View File

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

View File

@ -348,6 +348,20 @@ declare namespace memory {
export function reset(): void;
}
/** 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;
}
/** Table operations. */
declare namespace table {
/** Copies elements from a passive element segment to a table. */

View File

@ -0,0 +1,29 @@
var binaryen = require("binaryen");
var mod = new binaryen.Module();
var addType = mod.addFunctionType("iii", binaryen.i32, [ binaryen.i32, binaryen.i32 ]);
mod.addFunction("add", addType, [],
mod.i32.add(
mod.get_local(0, binaryen.i32),
mod.get_local(1, binaryen.i32)
)
);
mod.addFunctionExport("add", "add");
var testType = mod.addFunctionType("i", binaryen.i32, []);
mod.addFunction("test", testType, [],
mod.call("add", [
mod.i32.const(1),
mod.i32.const(2)
], binaryen.i32)
);
mod.addFunctionExport("test", "test");
binaryen.setOptimizeLevel(4);
binaryen.setShrinkLevel(0);
binaryen.setDebugInfo(false);
mod.runPasses(["precompute"]);
if (!mod.validate())
console.log("-> does not validate");
console.log(mod.emitText());

View File

@ -0,0 +1,19 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $i (func (result i32)))
(export "add" (func $add))
(export "test" (func $test))
(func $add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
(get_local $0)
(get_local $1)
)
)
(func $test (; 1 ;) (type $i) (result i32)
(call $add
(i32.const 1)
(i32.const 2)
)
)
)

View File

@ -167,7 +167,7 @@
(get_local $1)
)
)
(func $~lib/collector/itcm/gc.alloc (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(func $~lib/collector/itcm/__gc_allocate (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(if
(i32.gt_u
@ -178,8 +178,8 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 214)
(i32.const 4)
(i32.const 216)
(i32.const 2)
)
(unreachable)
)
@ -207,7 +207,13 @@
(i32.const 16)
)
)
(func $start (; 8 ;) (type $v)
(func $~lib/gc/gc.allocate (; 8 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(call $~lib/collector/itcm/__gc_allocate
(get_local $0)
(get_local $1)
)
)
(func $start (; 9 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.const 80)
)
@ -218,7 +224,7 @@
(i32.const 0)
)
(set_global $std/gc/obj
(call $~lib/collector/itcm/gc.alloc
(call $~lib/gc/gc.allocate
(i32.const 4)
(i32.const 0)
)

View File

@ -1,5 +1,5 @@
import "allocator/arena";
import { gc } from "collector/itcm";
import "collector/itcm";
// a class to test with
class MyObject {
@ -8,7 +8,7 @@ class MyObject {
function MyObject_visit(ref: usize): void { }
// allocate a managed instance
var obj = changetype<MyObject>(gc.alloc(offsetof<MyObject>(), MyObject_visit));
var obj = changetype<MyObject>(gc.allocate(offsetof<MyObject>(), MyObject_visit));
obj.a = 123;
var head = changetype<usize>(obj) - 16;

View File

@ -12,8 +12,6 @@
(global $~lib/internal/allocator/MAX_SIZE_32 i32 (i32.const 1073741824))
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
(global $NaN f64 (f64.const nan:0x8000000000000))
(global $Infinity f64 (f64.const inf))
(global $~lib/collector/itcm/State.INIT i32 (i32.const 0))
(global $~lib/collector/itcm/State.IDLE i32 (i32.const 1))
(global $~lib/collector/itcm/State.MARK i32 (i32.const 2))
@ -209,7 +207,7 @@
(get_local $1)
)
)
(func $~lib/collector/itcm/gc.alloc (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(func $~lib/collector/itcm/__gc_allocate (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(if
(i32.eqz
@ -225,8 +223,8 @@
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 214)
(i32.const 4)
(i32.const 216)
(i32.const 2)
)
(unreachable)
)
@ -250,12 +248,22 @@
(get_global $~lib/collector/itcm/set1)
(get_local $2)
)
(i32.add
(get_local $2)
(get_global $~lib/collector/itcm/ManagedObject.SIZE)
(block $~lib/collector/itcm/objToRef|inlined.0 (result i32)
(i32.add
(get_local $2)
(get_global $~lib/collector/itcm/ManagedObject.SIZE)
)
)
)
(func $start (; 8 ;) (type $v)
(func $~lib/gc/gc.allocate (; 8 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(call $~lib/collector/itcm/__gc_allocate
(get_local $0)
(get_local $1)
)
)
)
(func $start (; 9 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.and
(i32.add
@ -275,7 +283,7 @@
(get_global $~lib/collector/itcm/State.INIT)
)
(set_global $std/gc/obj
(call $~lib/collector/itcm/gc.alloc
(call $~lib/gc/gc.allocate
(i32.const 4)
(i32.const 0)
)