mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-28 23:21:26 +00:00
Make an interface around gc.* fwiw
This commit is contained in:
parent
34839353fd
commit
fafaf423b4
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
14
std/assembly/index.d.ts
vendored
14
std/assembly/index.d.ts
vendored
@ -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. */
|
||||
|
29
tests/binaryen/const-expr.js
Normal file
29
tests/binaryen/const-expr.js
Normal 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());
|
19
tests/binaryen/const-expr.wat
Normal file
19
tests/binaryen/const-expr.wat
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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)
|
||||
)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user