From fafaf423b43f0408438860e3af57520633702057 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Thu, 19 Jul 2018 16:15:56 +0200 Subject: [PATCH] Make an interface around gc.* fwiw --- std/assembly/allocator/arena.ts | 6 +- std/assembly/allocator/buddy.ts | 13 ++-- std/assembly/allocator/emscripten.ts | 13 ++-- std/assembly/allocator/system.ts | 13 ++-- std/assembly/allocator/tlsf.ts | 11 ++-- std/assembly/collector/itcm.ts | 92 +++++++++++++++------------- std/assembly/gc.ts | 23 +++++++ std/assembly/index.d.ts | 14 +++++ tests/binaryen/const-expr.js | 29 +++++++++ tests/binaryen/const-expr.wat | 19 ++++++ tests/compiler/std/gc.optimized.wat | 16 +++-- tests/compiler/std/gc.ts | 4 +- tests/compiler/std/gc.untouched.wat | 28 ++++++--- 13 files changed, 183 insertions(+), 98 deletions(-) create mode 100644 tests/binaryen/const-expr.js create mode 100644 tests/binaryen/const-expr.wat diff --git a/std/assembly/allocator/arena.ts b/std/assembly/allocator/arena.ts index 8adb8485..19042e99 100644 --- a/std/assembly/allocator/arena.ts +++ b/std/assembly/allocator/arena.ts @@ -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 { diff --git a/std/assembly/allocator/buddy.ts b/std/assembly/allocator/buddy.ts index 7157dd83..2aea2240 100644 --- a/std/assembly/allocator/buddy.ts +++ b/std/assembly/allocator/buddy.ts @@ -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(ptr_for_node(i, bucket))); } - -@global -export function __memory_reset(): void { - unreachable(); -} diff --git a/std/assembly/allocator/emscripten.ts b/std/assembly/allocator/emscripten.ts index f3c63f44..92956b76 100644 --- a/std/assembly/allocator/emscripten.ts +++ b/std/assembly/allocator/emscripten.ts @@ -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(); -} diff --git a/std/assembly/allocator/system.ts b/std/assembly/allocator/system.ts index eeaf552f..76b3293b 100644 --- a/std/assembly/allocator/system.ts +++ b/std/assembly/allocator/system.ts @@ -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(); -} diff --git a/std/assembly/allocator/tlsf.ts b/std/assembly/allocator/tlsf.ts index bf040141..fda68cfa 100644 --- a/std/assembly/allocator/tlsf.ts +++ b/std/assembly/allocator/tlsf.ts @@ -433,11 +433,10 @@ function fls(word: T): T { /** Reference to the initialized {@link Root} structure, once initialized. */ var ROOT: Root = changetype(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(); } diff --git a/std/assembly/collector/itcm.ts b/std/assembly/collector/itcm.ts index 4bbef614..691e34c9 100644 --- a/std/assembly/collector/itcm.ts +++ b/std/assembly/collector/itcm.ts @@ -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(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(memory.allocate(ManagedObject.SIZE + size)); - obj.makeWhite(); - obj.visitFn = visitFn; - set1.insert(obj); - return changetype(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(ref - ManagedObject.SIZE); +} + +@inline function objToRef(obj: ManagedObject): usize { + return changetype(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(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(); } diff --git a/std/assembly/gc.ts b/std/assembly/gc.ts index 59a72904..fbe72028 100644 --- a/std/assembly/gc.ts +++ b/std/assembly/gc.ts @@ -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 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(); + } } diff --git a/std/assembly/index.d.ts b/std/assembly/index.d.ts index 9a0b1096..5777a0bc 100644 --- a/std/assembly/index.d.ts +++ b/std/assembly/index.d.ts @@ -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. */ diff --git a/tests/binaryen/const-expr.js b/tests/binaryen/const-expr.js new file mode 100644 index 00000000..61b3ddca --- /dev/null +++ b/tests/binaryen/const-expr.js @@ -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()); diff --git a/tests/binaryen/const-expr.wat b/tests/binaryen/const-expr.wat new file mode 100644 index 00000000..936b198e --- /dev/null +++ b/tests/binaryen/const-expr.wat @@ -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) + ) + ) +) + diff --git a/tests/compiler/std/gc.optimized.wat b/tests/compiler/std/gc.optimized.wat index 67b5bea4..b1527fb1 100644 --- a/tests/compiler/std/gc.optimized.wat +++ b/tests/compiler/std/gc.optimized.wat @@ -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) ) diff --git a/tests/compiler/std/gc.ts b/tests/compiler/std/gc.ts index 4d0a5778..9c7ae1ed 100644 --- a/tests/compiler/std/gc.ts +++ b/tests/compiler/std/gc.ts @@ -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(gc.alloc(offsetof(), MyObject_visit)); +var obj = changetype(gc.allocate(offsetof(), MyObject_visit)); obj.a = 123; var head = changetype(obj) - 16; diff --git a/tests/compiler/std/gc.untouched.wat b/tests/compiler/std/gc.untouched.wat index 4b39ecc7..15b686ea 100644 --- a/tests/compiler/std/gc.untouched.wat +++ b/tests/compiler/std/gc.untouched.wat @@ -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) )