diff --git a/std/assembly/gc.ts b/std/assembly/gc.ts index 4edf24aa..e117ff23 100644 --- a/std/assembly/gc.ts +++ b/std/assembly/gc.ts @@ -1,16 +1,48 @@ /// +import { E_NOTIMPLEMENTED } from "./util/error"; + +// @ts-ignore +@lazy +var GC_ROOT = new Set(); + /** Garbage collector interface. */ export namespace gc { /** Whether the garbage collector interface is implemented. */ // @ts-ignore: decorator @lazy - export const IMPLEMENTED: bool = isDefined(__ref_collect); + export const implemented: bool = isDefined(__ref_collect); /** Performs a full garbage collection cycle. */ export function collect(): void { if (isDefined(__ref_collect)) __ref_collect(); - else WARNING("missing implementation: gc.collect"); + else throw new Error(E_NOTIMPLEMENTED); + } + + /** Retains a reference, making sure that it doesn't become collected. */ + export function retain(ref: usize): void { + var root = GC_ROOT; + if (!root.has(ref)) { + root.add(ref); + if (implemented) { + if (isDefined(__ref_link)) __ref_link(ref, changetype(root)); + else if (isDefined(__ref_retain)) __ref_retain(ref); + else assert(false); + } + } + } + + /** Releases a reference, allowing it to become collected. */ + export function release(ref: usize): void { + var root = GC_ROOT; + if (root.has(ref)) { + root.delete(ref); + if (implemented) { + if (isDefined(__ref_link)) __ref_unlink(ref, changetype(root)); + else if (isDefined(__ref_retain)) __ref_release(ref); + else assert(false); + } + } } } diff --git a/std/assembly/index.d.ts b/std/assembly/index.d.ts index 68a648d6..67f655cb 100644 --- a/std/assembly/index.d.ts +++ b/std/assembly/index.d.ts @@ -974,6 +974,8 @@ declare function bswap16(value: T): T; /** Memory operations. */ declare namespace memory { + /** Whether the memory managed interface is implemented. */ + export const implemented: bool; /** Returns the current memory size in units of pages. One page is 64kb. */ export function size(): i32; /** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */ @@ -985,9 +987,9 @@ declare namespace memory { /** Repeats `src` of length `srcLength` `count` times at `dst`. */ export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void; /** Copies elements from a passive element segment to a table. */ - // export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void; + export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void; /** Prevents further use of a passive element segment. */ - // export function drop(segmentIndex: u32): void; + export function drop(segmentIndex: u32): void; /** Copies elements from one region of a table to another region. */ export function allocate(size: usize): usize; /** Disposes a chunk of memory by its pointer. */ @@ -1000,20 +1002,24 @@ declare namespace memory { /** Garbage collector operations. */ declare namespace gc { - /** Allocates a managed object identified by its visitor function. */ - export function allocate(size: usize, visitFn: (ref: usize) => void): usize; + /** Whether the garbage collector interface is implemented. */ + export const implemented: bool; /** Performs a full garbage collection cycle. */ export function collect(): void; + /** Retains a reference, making sure that it doesn't become collected. */ + export function retain(ref: usize): void; + /** Releases a reference, allowing it to become collected. */ + export function release(ref: usize): void; } /** Table operations. */ declare namespace table { /** Copies elements from a passive element segment to a table. */ - // export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void; + export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void; /** Prevents further use of a passive element segment. */ - // export function drop(elementIndex: u32): void; + export function drop(elementIndex: u32): void; /** Copies elements from one region of a table to another region. */ - // export function copy(dest: u32, src: u32, n: u32): void; + export function copy(dest: u32, src: u32, n: u32): void; } /** Class representing a generic, fixed-length raw binary data buffer. */ diff --git a/std/assembly/memory.ts b/std/assembly/memory.ts index 46ee6c44..6865c362 100644 --- a/std/assembly/memory.ts +++ b/std/assembly/memory.ts @@ -1,6 +1,7 @@ /// import { memcmp, memmove, memset } from "./util/memory"; +import { E_NOTIMPLEMENTED } from "./util/error"; // @ts-ignore: decorator @builtin @@ -9,6 +10,11 @@ export declare const HEAP_BASE: usize; /** Memory manager interface. */ export namespace memory { + /** Whether the memory managed interface is implemented. */ + // @ts-ignore: decorator + @lazy + export const implemented: bool = isDefined(__mem_allocate); + /** Gets the size of the memory in pages. */ // @ts-ignore: decorator @builtin @@ -37,14 +43,14 @@ export namespace memory { // @ts-ignore: decorator @unsafe export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void { - unreachable(); // not yet implemented + throw new Error(E_NOTIMPLEMENTED); } /** Drops a memory segment. */ // @ts-ignore: decorator @unsafe export function drop(segmentIndex: u32): void { - unreachable(); // not yet implemented + throw new Error(E_NOTIMPLEMENTED); } /** Dynamically allocates a section of memory and returns its address. */ @@ -52,7 +58,7 @@ export namespace memory { @unsafe export function allocate(size: usize): usize { if (isDefined(__mem_allocate)) return __mem_allocate(size); - else return unreachable(); + else throw new Error(E_NOTIMPLEMENTED); } /** Dynamically frees a section of memory by the previously allocated address. */ @@ -60,7 +66,7 @@ export namespace memory { @unsafe export function free(ptr: usize): void { if (isDefined(__mem_free)) __mem_free(ptr); - else unreachable(); + else throw new Error(E_NOTIMPLEMENTED); } /** Resets the memory to its initial state. Arena allocator only. */ @@ -68,7 +74,7 @@ export namespace memory { @unsafe export function reset(): void { if (isDefined(__mem_reset)) __mem_reset(); - else unreachable(); + else throw new Error(E_NOTIMPLEMENTED); } /** Repeats a section of memory at a specific address. */ diff --git a/std/assembly/table.ts b/std/assembly/table.ts index cc312ade..5598bc88 100644 --- a/std/assembly/table.ts +++ b/std/assembly/table.ts @@ -1,14 +1,16 @@ +import { E_NOTIMPLEMENTED } from "./util/error"; + export namespace table { export function copy(dst: u32, src: u32, n: u32): void { - ERROR("not implemented: table.copy"); + throw new Error(E_NOTIMPLEMENTED); } export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void { - ERROR("not implemented: table.init"); + throw new Error(E_NOTIMPLEMENTED); } export function drop(elementIndex: u32): void { - ERROR("not implemented: table.drop"); + throw new Error(E_NOTIMPLEMENTED); } } diff --git a/std/assembly/util/error.ts b/std/assembly/util/error.ts index c9c1dbc1..cbee8cbf 100644 --- a/std/assembly/util/error.ts +++ b/std/assembly/util/error.ts @@ -16,3 +16,7 @@ export const E_EMPTYARRAY: string = "Array is empty"; // @ts-ignore: decorator @lazy @inline export const E_HOLEYARRAY: string = "Element type must be nullable if array is holey"; + +// @ts-ignore: decorator +@lazy @inline +export const E_NOTIMPLEMENTED: string = "Not implemented"; diff --git a/tests/compiler/gc.optimized.wat b/tests/compiler/gc.optimized.wat new file mode 100644 index 00000000..36430769 --- /dev/null +++ b/tests/compiler/gc.optimized.wat @@ -0,0 +1,1118 @@ +(module + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$viiddddd (func (param i32 i32 f64 f64 f64 f64 f64))) + (type $FUNCSIG$vii (func (param i32 i32))) + (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) + (type $FUNCSIG$i (func (result i32))) + (import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32))) + (import "env" "trace" (func $~lib/env/trace (param i32 i32 f64 f64 f64 f64 f64))) + (memory $0 1) + (data (i32.const 8) "\02\00\00\00\1e") + (data (i32.const 24) "~\00l\00i\00b\00/\00r\00u\00n\00t\00i\00m\00e\00.\00t\00s") + (data (i32.const 56) "\02\00\00\00\16") + (data (i32.const 72) "g\00c\00.\00r\00e\00g\00i\00s\00t\00e\00r") + (data (i32.const 96) "\02\00\00\00\n") + (data (i32.const 112) "g\00c\00.\00t\00s") + (data (i32.const 128) "\02\00\00\00&") + (data (i32.const 144) "~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s") + (data (i32.const 184) "\02\00\00\00\0e") + (data (i32.const 200) "g\00c\00.\00l\00i\00n\00k") + (data (i32.const 216) "\02\00\00\00\12") + (data (i32.const 232) "g\00c\00.\00u\00n\00l\00i\00n\00k") + (data (i32.const 256) "\02\00\00\00\14") + (data (i32.const 272) "g\00c\00.\00c\00o\00l\00l\00e\00c\00t") + (table $0 1 funcref) + (elem (i32.const 0) $null) + (global $gc/_dummy/collect_count (mut i32) (i32.const 0)) + (global $gc/_dummy/register_count (mut i32) (i32.const 0)) + (global $gc/_dummy/register_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/link_count (mut i32) (i32.const 0)) + (global $gc/_dummy/link_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/link_parentRef (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_count (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_parentRef (mut i32) (i32.const 0)) + (global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0)) + (global $~lib/allocator/arena/offset (mut i32) (i32.const 0)) + (global $~lib/gc/gc.implemented i32 (i32.const 1)) + (global $~lib/gc/GC_ROOT (mut i32) (i32.const 0)) + (global $~lib/started (mut i32) (i32.const 0)) + (global $~lib/capabilities i32 (i32.const 2)) + (export "memory" (memory $0)) + (export "table" (table $0)) + (export "main" (func $gc/main)) + (export "gc.implemented" (global $~lib/gc/gc.implemented)) + (export "gc.collect" (func $~lib/gc/gc.collect)) + (export "gc.retain" (func $~lib/gc/gc.retain)) + (export "gc.release" (func $~lib/gc/gc.release)) + (export ".capabilities" (global $~lib/capabilities)) + (func $~lib/allocator/arena/__mem_allocate (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.const 1073741824 + i32.gt_u + if + unreachable + end + global.get $~lib/allocator/arena/offset + local.tee $1 + local.get $0 + i32.const 1 + local.get $0 + i32.const 1 + i32.gt_u + select + i32.add + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $0 + current_memory + local.tee $2 + i32.const 16 + i32.shl + i32.gt_u + if + local.get $2 + local.get $0 + local.get $1 + i32.sub + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $3 + local.get $2 + local.get $3 + i32.gt_s + select + grow_memory + i32.const 0 + i32.lt_s + if + local.get $3 + grow_memory + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/allocator/arena/offset + local.get $1 + ) + (func $~lib/runtime/allocate (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + i32.const 1 + i32.const 32 + local.get $0 + i32.const 15 + i32.add + i32.clz + i32.sub + i32.shl + call $~lib/allocator/arena/__mem_allocate + local.tee $1 + i32.const -1520547049 + i32.store + local.get $1 + local.get $0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $1 + i32.const 16 + i32.add + ) + (func $gc/_dummy/__ref_register (; 4 ;) (type $FUNCSIG$vi) (param $0 i32) + i32.const 72 + i32.const 1 + local.get $0 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/register_count + i32.const 1 + i32.add + global.set $gc/_dummy/register_count + local.get $0 + global.set $gc/_dummy/register_ref + ) + (func $~lib/runtime/register (; 5 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $0 + i32.const 292 + i32.le_u + if + i32.const 0 + i32.const 24 + i32.const 149 + i32.const 4 + call $~lib/env/abort + unreachable + end + local.get $0 + i32.const 16 + i32.sub + local.tee $2 + i32.load + i32.const -1520547049 + i32.ne + if + i32.const 0 + i32.const 24 + i32.const 151 + i32.const 4 + call $~lib/env/abort + unreachable + end + local.get $2 + local.get $1 + i32.store + local.get $0 + call $gc/_dummy/__ref_register + local.get $0 + ) + (func $~lib/memory/memory.fill (; 6 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + block $~lib/util/memory/memset|inlined.0 + local.get $1 + i32.eqz + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store8 + local.get $0 + local.get $1 + i32.add + i32.const 1 + i32.sub + i32.const 0 + i32.store8 + local.get $1 + i32.const 2 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 1 + i32.add + i32.const 0 + i32.store8 + local.get $0 + i32.const 2 + i32.add + i32.const 0 + i32.store8 + local.get $0 + local.get $1 + i32.add + local.tee $2 + i32.const 2 + i32.sub + i32.const 0 + i32.store8 + local.get $2 + i32.const 3 + i32.sub + i32.const 0 + i32.store8 + local.get $1 + i32.const 6 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 3 + i32.add + i32.const 0 + i32.store8 + local.get $0 + local.get $1 + i32.add + i32.const 4 + i32.sub + i32.const 0 + i32.store8 + local.get $1 + i32.const 8 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $1 + i32.const 0 + local.get $0 + i32.sub + i32.const 3 + i32.and + local.tee $1 + i32.sub + local.set $2 + local.get $0 + local.get $1 + i32.add + local.tee $0 + i32.const 0 + i32.store + local.get $2 + i32.const -4 + i32.and + local.tee $1 + local.get $0 + i32.add + i32.const 4 + i32.sub + i32.const 0 + i32.store + local.get $1 + i32.const 8 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 4 + i32.add + i32.const 0 + i32.store + local.get $0 + i32.const 8 + i32.add + i32.const 0 + i32.store + local.get $0 + local.get $1 + i32.add + local.tee $2 + i32.const 12 + i32.sub + i32.const 0 + i32.store + local.get $2 + i32.const 8 + i32.sub + i32.const 0 + i32.store + local.get $1 + i32.const 24 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 12 + i32.add + i32.const 0 + i32.store + local.get $0 + i32.const 16 + i32.add + i32.const 0 + i32.store + local.get $0 + i32.const 20 + i32.add + i32.const 0 + i32.store + local.get $0 + i32.const 24 + i32.add + i32.const 0 + i32.store + local.get $0 + local.get $1 + i32.add + local.tee $2 + i32.const 28 + i32.sub + i32.const 0 + i32.store + local.get $2 + i32.const 24 + i32.sub + i32.const 0 + i32.store + local.get $2 + i32.const 20 + i32.sub + i32.const 0 + i32.store + local.get $2 + i32.const 16 + i32.sub + i32.const 0 + i32.store + local.get $0 + i32.const 4 + i32.and + i32.const 24 + i32.add + local.tee $2 + local.get $0 + i32.add + local.set $0 + local.get $1 + local.get $2 + i32.sub + local.set $1 + loop $continue|0 + local.get $1 + i32.const 32 + i32.ge_u + if + local.get $0 + i64.const 0 + i64.store + local.get $0 + i32.const 8 + i32.add + i64.const 0 + i64.store + local.get $0 + i32.const 16 + i32.add + i64.const 0 + i64.store + local.get $0 + i32.const 24 + i32.add + i64.const 0 + i64.store + local.get $1 + i32.const 32 + i32.sub + local.set $1 + local.get $0 + i32.const 32 + i32.add + local.set $0 + br $continue|0 + end + end + end + ) + (func $~lib/arraybuffer/ArrayBuffer#constructor (; 7 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.const 1073741808 + i32.gt_u + if + i32.const 0 + i32.const 144 + i32.const 25 + i32.const 43 + call $~lib/env/abort + unreachable + end + local.get $0 + call $~lib/runtime/allocate + local.tee $1 + local.get $0 + call $~lib/memory/memory.fill + local.get $1 + i32.const 4 + call $~lib/runtime/register + ) + (func $gc/_dummy/__ref_link (; 8 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + i32.const 200 + i32.const 2 + local.get $0 + f64.convert_i32_u + local.get $1 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/link_count + i32.const 1 + i32.add + global.set $gc/_dummy/link_count + local.get $0 + global.set $gc/_dummy/link_ref + local.get $0 + global.set $gc/_dummy/link_parentRef + ) + (func $gc/_dummy/__ref_unlink (; 9 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + i32.const 232 + i32.const 2 + local.get $0 + f64.convert_i32_u + local.get $1 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/unlink_count + i32.const 1 + i32.add + global.set $gc/_dummy/unlink_count + local.get $0 + global.set $gc/_dummy/unlink_ref + local.get $1 + global.set $gc/_dummy/unlink_parentRef + ) + (func $~lib/set/Set#clear (; 10 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + local.tee $2 + local.set $1 + i32.const 16 + call $~lib/arraybuffer/ArrayBuffer#constructor + local.tee $0 + local.get $1 + i32.load + local.tee $3 + i32.ne + if + local.get $3 + if + local.get $3 + local.get $2 + call $gc/_dummy/__ref_unlink + end + local.get $0 + local.get $2 + call $gc/_dummy/__ref_link + end + local.get $1 + local.get $0 + i32.store + local.get $1 + i32.const 3 + i32.store offset=4 + i32.const 32 + call $~lib/arraybuffer/ArrayBuffer#constructor + local.tee $0 + local.get $1 + local.tee $2 + i32.load offset=8 + local.tee $3 + i32.ne + if + local.get $3 + if + local.get $3 + local.get $2 + call $gc/_dummy/__ref_unlink + end + local.get $0 + local.get $2 + call $gc/_dummy/__ref_link + end + local.get $1 + local.get $0 + i32.store offset=8 + local.get $1 + i32.const 4 + i32.store offset=12 + local.get $1 + i32.const 0 + i32.store offset=16 + local.get $1 + i32.const 0 + i32.store offset=20 + ) + (func $~lib/set/Set#constructor (; 11 ;) (type $FUNCSIG$i) (result i32) + (local $0 i32) + i32.const 24 + call $~lib/runtime/allocate + i32.const 3 + call $~lib/runtime/register + local.tee $0 + i32.const 0 + i32.store + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $0 + i32.const 0 + i32.store offset=20 + local.get $0 + call $~lib/set/Set#clear + local.get $0 + ) + (func $~lib/util/hash/hash32 (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.const 255 + i32.and + i32.const -2128831035 + i32.xor + i32.const 16777619 + i32.mul + local.get $0 + i32.const 8 + i32.shr_u + i32.const 255 + i32.and + i32.xor + i32.const 16777619 + i32.mul + local.get $0 + i32.const 16 + i32.shr_u + i32.const 255 + i32.and + i32.xor + i32.const 16777619 + i32.mul + local.get $0 + i32.const 24 + i32.shr_u + i32.xor + i32.const 16777619 + i32.mul + ) + (func $~lib/set/Set#find (; 13 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + local.get $0 + i32.load + local.get $0 + i32.load offset=4 + local.get $2 + i32.and + i32.const 2 + i32.shl + i32.add + i32.load + local.set $2 + loop $continue|0 + local.get $2 + if + local.get $2 + i32.load offset=4 + i32.const 1 + i32.and + i32.eqz + local.tee $0 + if + local.get $2 + i32.load + local.get $1 + i32.eq + local.set $0 + end + local.get $0 + if + local.get $2 + return + end + local.get $2 + i32.load offset=4 + i32.const -2 + i32.and + local.set $2 + br $continue|0 + end + end + i32.const 0 + ) + (func $~lib/set/Set#has (; 14 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + local.get $0 + local.get $1 + local.get $1 + call $~lib/util/hash/hash32 + call $~lib/set/Set#find + i32.const 0 + i32.ne + ) + (func $~lib/set/Set#rehash (; 15 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + local.get $1 + i32.const 1 + i32.add + local.tee $2 + i32.const 2 + i32.shl + call $~lib/arraybuffer/ArrayBuffer#constructor + local.set $4 + local.get $2 + f64.convert_i32_s + f64.const 2.6666666666666665 + f64.mul + i32.trunc_f64_s + local.tee $6 + i32.const 3 + i32.shl + call $~lib/arraybuffer/ArrayBuffer#constructor + local.set $5 + local.get $0 + i32.load offset=8 + local.tee $2 + local.get $0 + i32.load offset=16 + i32.const 3 + i32.shl + i32.add + local.set $7 + local.get $5 + local.set $3 + loop $continue|0 + local.get $2 + local.get $7 + i32.ne + if + local.get $2 + i32.load offset=4 + i32.const 1 + i32.and + i32.eqz + if + local.get $3 + local.get $2 + i32.load + i32.store + local.get $3 + local.get $2 + i32.load + call $~lib/util/hash/hash32 + local.get $1 + i32.and + i32.const 2 + i32.shl + local.get $4 + i32.add + local.tee $8 + i32.load + i32.store offset=4 + local.get $8 + local.get $3 + i32.store + local.get $3 + i32.const 8 + i32.add + local.set $3 + end + local.get $2 + i32.const 8 + i32.add + local.set $2 + br $continue|0 + end + end + local.get $0 + local.tee $3 + local.set $2 + local.get $4 + local.tee $0 + local.get $2 + i32.load + local.tee $4 + i32.ne + if + local.get $4 + if + local.get $4 + local.get $3 + call $gc/_dummy/__ref_unlink + end + local.get $0 + local.get $3 + call $gc/_dummy/__ref_link + end + local.get $2 + local.get $0 + i32.store + local.get $2 + local.get $1 + i32.store offset=4 + local.get $5 + local.tee $0 + local.get $2 + local.tee $1 + i32.load offset=8 + local.tee $3 + i32.ne + if + local.get $3 + if + local.get $3 + local.get $2 + call $gc/_dummy/__ref_unlink + end + local.get $0 + local.get $2 + call $gc/_dummy/__ref_link + end + local.get $1 + local.get $0 + i32.store offset=8 + local.get $1 + local.get $6 + i32.store offset=12 + local.get $1 + local.get $1 + i32.load offset=20 + i32.store offset=16 + ) + (func $~lib/set/Set#add (; 16 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + local.get $0 + local.get $1 + local.get $1 + call $~lib/util/hash/hash32 + local.tee $3 + call $~lib/set/Set#find + i32.eqz + if + local.get $0 + i32.load offset=16 + local.get $0 + i32.load offset=12 + i32.eq + if + local.get $0 + local.get $0 + i32.load offset=20 + local.get $0 + i32.load offset=12 + f64.convert_i32_s + f64.const 0.75 + f64.mul + i32.trunc_f64_s + i32.lt_s + if (result i32) + local.get $0 + i32.load offset=4 + else + local.get $0 + i32.load offset=4 + i32.const 1 + i32.shl + i32.const 1 + i32.or + end + call $~lib/set/Set#rehash + end + local.get $0 + i32.load offset=8 + local.set $2 + local.get $0 + local.get $0 + i32.load offset=16 + local.tee $4 + i32.const 1 + i32.add + i32.store offset=16 + local.get $4 + i32.const 3 + i32.shl + local.get $2 + i32.add + local.tee $2 + local.get $1 + i32.store + local.get $0 + local.get $0 + i32.load offset=20 + i32.const 1 + i32.add + i32.store offset=20 + local.get $2 + local.get $0 + i32.load + local.get $0 + i32.load offset=4 + local.get $3 + i32.and + i32.const 2 + i32.shl + i32.add + local.tee $0 + i32.load + i32.store offset=4 + local.get $0 + local.get $2 + i32.store + end + ) + (func $~lib/gc/gc.retain (; 17 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + global.get $~lib/gc/GC_ROOT + local.tee $1 + local.get $0 + call $~lib/set/Set#has + i32.eqz + if + local.get $1 + local.get $0 + call $~lib/set/Set#add + local.get $0 + local.get $1 + call $gc/_dummy/__ref_link + end + ) + (func $~lib/set/Set#delete (; 18 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $0 + local.get $1 + local.get $1 + call $~lib/util/hash/hash32 + call $~lib/set/Set#find + local.tee $1 + i32.eqz + if + return + end + local.get $1 + local.get $1 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=20 + i32.const 1 + i32.sub + i32.store offset=20 + local.get $0 + i32.load offset=4 + i32.const 1 + i32.shr_u + local.tee $2 + i32.const 1 + i32.add + i32.const 4 + local.get $0 + i32.load offset=20 + local.tee $1 + i32.const 4 + local.get $1 + i32.gt_u + select + i32.ge_u + local.tee $1 + if (result i32) + local.get $0 + i32.load offset=20 + local.get $0 + i32.load offset=12 + f64.convert_i32_s + f64.const 0.75 + f64.mul + i32.trunc_f64_s + i32.lt_s + else + local.get $1 + end + if + local.get $0 + local.get $2 + call $~lib/set/Set#rehash + end + ) + (func $~lib/gc/gc.release (; 19 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + global.get $~lib/gc/GC_ROOT + local.tee $1 + local.get $0 + call $~lib/set/Set#has + if + local.get $1 + local.get $0 + call $~lib/set/Set#delete + local.get $0 + local.get $1 + call $gc/_dummy/__ref_unlink + end + ) + (func $~lib/gc/gc.collect (; 20 ;) (type $FUNCSIG$v) + i32.const 272 + i32.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/collect_count + i32.const 1 + i32.add + global.set $gc/_dummy/collect_count + ) + (func $gc/main (; 21 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + global.get $~lib/started + i32.eqz + if + i32.const 296 + global.set $~lib/allocator/arena/startOffset + global.get $~lib/allocator/arena/startOffset + global.set $~lib/allocator/arena/offset + call $~lib/set/Set#constructor + global.set $~lib/gc/GC_ROOT + i32.const 1 + global.set $~lib/started + end + i32.const 0 + call $~lib/runtime/allocate + i32.const 1 + call $~lib/runtime/register + local.set $2 + global.get $gc/_dummy/link_count + local.set $3 + global.get $gc/_dummy/unlink_count + local.set $0 + global.get $gc/_dummy/collect_count + local.set $1 + local.get $2 + call $~lib/gc/gc.retain + global.get $gc/_dummy/link_count + local.get $3 + i32.const 1 + i32.add + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 18 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $0 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 19 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $1 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 20 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/link_count + local.set $3 + global.get $gc/_dummy/unlink_count + local.set $0 + global.get $gc/_dummy/collect_count + local.set $1 + local.get $2 + call $~lib/gc/gc.release + global.get $gc/_dummy/link_count + local.get $3 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 27 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $0 + i32.const 1 + i32.add + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 28 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $1 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 29 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/link_count + local.set $0 + global.get $gc/_dummy/unlink_count + local.set $1 + global.get $gc/_dummy/collect_count + local.set $2 + call $~lib/gc/gc.collect + global.get $gc/_dummy/link_count + local.get $0 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 36 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $1 + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 37 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $2 + i32.const 1 + i32.add + i32.ne + if + i32.const 0 + i32.const 112 + i32.const 38 + i32.const 2 + call $~lib/env/abort + unreachable + end + ) + (func $null (; 22 ;) (type $FUNCSIG$v) + nop + ) +) diff --git a/tests/compiler/gc.ts b/tests/compiler/gc.ts new file mode 100644 index 00000000..af6cb670 --- /dev/null +++ b/tests/compiler/gc.ts @@ -0,0 +1,39 @@ +import "allocator/arena"; +import { link_count, unlink_count, collect_count } from "./gc/_dummy"; +export { gc }; + +class Ref {} + +@start export function main(): void { + var ref = new Ref(); + + assert(gc.implemented); + + var previous_link_count = link_count; + var previous_unlink_count = unlink_count; + var previous_collect_count = collect_count; + + gc.retain(changetype(ref)); + + assert(link_count == previous_link_count + 1); + assert(unlink_count == previous_unlink_count); + assert(collect_count == previous_collect_count); + previous_link_count = link_count; + previous_unlink_count = unlink_count; + previous_collect_count = collect_count; + + gc.release(changetype(ref)); + + assert(link_count == previous_link_count); + assert(unlink_count == previous_unlink_count + 1); + assert(collect_count == previous_collect_count); + previous_link_count = link_count; + previous_unlink_count = unlink_count; + previous_collect_count = collect_count; + + gc.collect(); + + assert(link_count == previous_link_count); + assert(unlink_count == previous_unlink_count); + assert(collect_count == previous_collect_count + 1); +} diff --git a/tests/compiler/gc.untouched.wat b/tests/compiler/gc.untouched.wat new file mode 100644 index 00000000..607482d5 --- /dev/null +++ b/tests/compiler/gc.untouched.wat @@ -0,0 +1,1349 @@ +(module + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$viiddddd (func (param i32 i32 f64 f64 f64 f64 f64))) + (type $FUNCSIG$viii (func (param i32 i32 i32))) + (type $FUNCSIG$vii (func (param i32 i32))) + (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) + (import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32))) + (import "env" "trace" (func $~lib/env/trace (param i32 i32 f64 f64 f64 f64 f64))) + (memory $0 1) + (data (i32.const 8) "\02\00\00\00\1e\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00r\00u\00n\00t\00i\00m\00e\00.\00t\00s\00") + (data (i32.const 56) "\02\00\00\00\16\00\00\00\00\00\00\00\00\00\00\00g\00c\00.\00r\00e\00g\00i\00s\00t\00e\00r\00") + (data (i32.const 96) "\02\00\00\00\n\00\00\00\00\00\00\00\00\00\00\00g\00c\00.\00t\00s\00") + (data (i32.const 128) "\02\00\00\00&\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s\00") + (data (i32.const 184) "\02\00\00\00\0e\00\00\00\00\00\00\00\00\00\00\00g\00c\00.\00l\00i\00n\00k\00") + (data (i32.const 216) "\02\00\00\00\12\00\00\00\00\00\00\00\00\00\00\00g\00c\00.\00u\00n\00l\00i\00n\00k\00") + (data (i32.const 256) "\02\00\00\00\14\00\00\00\00\00\00\00\00\00\00\00g\00c\00.\00c\00o\00l\00l\00e\00c\00t\00") + (table $0 1 funcref) + (elem (i32.const 0) $null) + (global $gc/_dummy/collect_count (mut i32) (i32.const 0)) + (global $gc/_dummy/register_count (mut i32) (i32.const 0)) + (global $gc/_dummy/register_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/link_count (mut i32) (i32.const 0)) + (global $gc/_dummy/link_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/link_parentRef (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_count (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_ref (mut i32) (i32.const 0)) + (global $gc/_dummy/unlink_parentRef (mut i32) (i32.const 0)) + (global $~lib/runtime/HEADER_SIZE i32 (i32.const 16)) + (global $~lib/runtime/HEADER_MAGIC i32 (i32.const -1520547049)) + (global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0)) + (global $~lib/allocator/arena/offset (mut i32) (i32.const 0)) + (global $~lib/ASC_NO_ASSERT i32 (i32.const 0)) + (global $~lib/gc/gc.implemented i32 (i32.const 1)) + (global $~lib/runtime/MAX_BYTELENGTH i32 (i32.const 1073741808)) + (global $~lib/gc/GC_ROOT (mut i32) (i32.const 0)) + (global $~lib/started (mut i32) (i32.const 0)) + (global $~lib/memory/HEAP_BASE i32 (i32.const 292)) + (global $~lib/capabilities i32 (i32.const 2)) + (export "memory" (memory $0)) + (export "table" (table $0)) + (export "main" (func $gc/main)) + (export "gc.implemented" (global $~lib/gc/gc.implemented)) + (export "gc.collect" (func $~lib/gc/gc.collect)) + (export "gc.retain" (func $~lib/gc/gc.retain)) + (export "gc.release" (func $~lib/gc/gc.release)) + (export ".capabilities" (global $~lib/capabilities)) + (func $~lib/runtime/ADJUSTOBLOCK (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + i32.const 1 + i32.const 32 + local.get $0 + global.get $~lib/runtime/HEADER_SIZE + i32.add + i32.const 1 + i32.sub + i32.clz + i32.sub + i32.shl + ) + (func $~lib/allocator/arena/__mem_allocate (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $0 + i32.const 1073741824 + i32.gt_u + if + unreachable + end + global.get $~lib/allocator/arena/offset + local.set $1 + local.get $1 + local.get $0 + local.tee $2 + i32.const 1 + local.tee $3 + local.get $2 + local.get $3 + i32.gt_u + select + i32.add + i32.const 7 + i32.add + i32.const 7 + i32.const -1 + i32.xor + i32.and + local.set $4 + current_memory + local.set $5 + local.get $4 + local.get $5 + i32.const 16 + i32.shl + i32.gt_u + if + local.get $4 + local.get $1 + i32.sub + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $2 + local.get $5 + local.tee $3 + local.get $2 + local.tee $6 + local.get $3 + local.get $6 + i32.gt_s + select + local.set $3 + local.get $3 + grow_memory + i32.const 0 + i32.lt_s + if + local.get $2 + grow_memory + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $4 + global.set $~lib/allocator/arena/offset + local.get $1 + ) + (func $~lib/memory/memory.allocate (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/allocator/arena/__mem_allocate + return + ) + (func $~lib/runtime/allocate (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + call $~lib/runtime/ADJUSTOBLOCK + call $~lib/memory/memory.allocate + local.set $1 + local.get $1 + global.get $~lib/runtime/HEADER_MAGIC + i32.store + local.get $1 + local.get $0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $1 + global.get $~lib/runtime/HEADER_SIZE + i32.add + ) + (func $gc/_dummy/__ref_register (; 6 ;) (type $FUNCSIG$vi) (param $0 i32) + i32.const 72 + i32.const 1 + local.get $0 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/register_count + i32.const 1 + i32.add + global.set $gc/_dummy/register_count + local.get $0 + global.set $gc/_dummy/register_ref + ) + (func $~lib/runtime/register (; 7 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $0 + global.get $~lib/memory/HEAP_BASE + i32.gt_u + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 149 + i32.const 4 + call $~lib/env/abort + unreachable + end + local.get $0 + global.get $~lib/runtime/HEADER_SIZE + i32.sub + local.set $2 + local.get $2 + i32.load + global.get $~lib/runtime/HEADER_MAGIC + i32.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 151 + i32.const 4 + call $~lib/env/abort + unreachable + end + local.get $2 + local.get $1 + i32.store + local.get $0 + call $gc/_dummy/__ref_register + local.get $0 + ) + (func $gc/Ref#constructor (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + call $~lib/runtime/allocate + i32.const 1 + call $~lib/runtime/register + local.set $0 + end + local.get $0 + ) + (func $~lib/memory/memory.fill (; 9 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i64) + block $~lib/util/memory/memset|inlined.0 + local.get $2 + i32.eqz + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $0 + local.get $1 + i32.store8 + local.get $0 + local.get $2 + i32.add + i32.const 1 + i32.sub + local.get $1 + i32.store8 + local.get $2 + i32.const 2 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $0 + i32.const 1 + i32.add + local.get $1 + i32.store8 + local.get $0 + i32.const 2 + i32.add + local.get $1 + i32.store8 + local.get $0 + local.get $2 + i32.add + i32.const 2 + i32.sub + local.get $1 + i32.store8 + local.get $0 + local.get $2 + i32.add + i32.const 3 + i32.sub + local.get $1 + i32.store8 + local.get $2 + i32.const 6 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $0 + i32.const 3 + i32.add + local.get $1 + i32.store8 + local.get $0 + local.get $2 + i32.add + i32.const 4 + i32.sub + local.get $1 + i32.store8 + local.get $2 + i32.const 8 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + i32.const 0 + local.get $0 + i32.sub + i32.const 3 + i32.and + local.set $5 + local.get $0 + local.get $5 + i32.add + local.set $0 + local.get $2 + local.get $5 + i32.sub + local.set $2 + local.get $2 + i32.const -4 + i32.and + local.set $2 + i32.const -1 + i32.const 255 + i32.div_u + local.get $1 + i32.const 255 + i32.and + i32.mul + local.set $4 + local.get $0 + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 4 + i32.sub + local.get $4 + i32.store + local.get $2 + i32.const 8 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.store + local.get $0 + i32.const 8 + i32.add + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 12 + i32.sub + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 8 + i32.sub + local.get $4 + i32.store + local.get $2 + i32.const 24 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.store + local.get $0 + i32.const 16 + i32.add + local.get $4 + i32.store + local.get $0 + i32.const 20 + i32.add + local.get $4 + i32.store + local.get $0 + i32.const 24 + i32.add + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 28 + i32.sub + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 24 + i32.sub + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 20 + i32.sub + local.get $4 + i32.store + local.get $0 + local.get $2 + i32.add + i32.const 16 + i32.sub + local.get $4 + i32.store + i32.const 24 + local.get $0 + i32.const 4 + i32.and + i32.add + local.set $5 + local.get $0 + local.get $5 + i32.add + local.set $0 + local.get $2 + local.get $5 + i32.sub + local.set $2 + local.get $4 + i64.extend_i32_u + local.get $4 + i64.extend_i32_u + i64.const 32 + i64.shl + i64.or + local.set $6 + block $break|0 + loop $continue|0 + local.get $2 + i32.const 32 + i32.ge_u + if + block + local.get $0 + local.get $6 + i64.store + local.get $0 + i32.const 8 + i32.add + local.get $6 + i64.store + local.get $0 + i32.const 16 + i32.add + local.get $6 + i64.store + local.get $0 + i32.const 24 + i32.add + local.get $6 + i64.store + local.get $2 + i32.const 32 + i32.sub + local.set $2 + local.get $0 + i32.const 32 + i32.add + local.set $0 + end + br $continue|0 + end + end + end + end + ) + (func $~lib/arraybuffer/ArrayBuffer#constructor (; 10 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $1 + global.get $~lib/runtime/MAX_BYTELENGTH + i32.gt_u + if + i32.const 0 + i32.const 144 + i32.const 25 + i32.const 43 + call $~lib/env/abort + unreachable + end + block $~lib/runtime/ALLOCATE|inlined.0 (result i32) + local.get $1 + local.set $2 + local.get $2 + call $~lib/runtime/allocate + end + local.set $3 + local.get $3 + i32.const 0 + local.get $1 + call $~lib/memory/memory.fill + block $~lib/runtime/REGISTER<~lib/arraybuffer/ArrayBuffer>|inlined.0 (result i32) + local.get $3 + local.set $2 + local.get $2 + i32.const 4 + call $~lib/runtime/register + end + ) + (func $gc/_dummy/__ref_link (; 11 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + i32.const 200 + i32.const 2 + local.get $0 + f64.convert_i32_u + local.get $1 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/link_count + i32.const 1 + i32.add + global.set $gc/_dummy/link_count + local.get $0 + global.set $gc/_dummy/link_ref + local.get $0 + global.set $gc/_dummy/link_parentRef + ) + (func $gc/_dummy/__ref_unlink (; 12 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + i32.const 232 + i32.const 2 + local.get $0 + f64.convert_i32_u + local.get $1 + f64.convert_i32_u + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/unlink_count + i32.const 1 + i32.add + global.set $gc/_dummy/unlink_count + local.get $0 + global.set $gc/_dummy/unlink_ref + local.get $1 + global.set $gc/_dummy/unlink_parentRef + ) + (func $~lib/set/Set#clear (; 13 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + local.tee $1 + i32.const 0 + i32.const 16 + call $~lib/arraybuffer/ArrayBuffer#constructor + local.tee $2 + local.get $1 + i32.load + local.tee $3 + i32.ne + if (result i32) + local.get $3 + if + local.get $3 + local.get $1 + call $gc/_dummy/__ref_unlink + end + local.get $2 + local.get $1 + call $gc/_dummy/__ref_link + local.get $2 + else + local.get $2 + end + i32.store + local.get $0 + i32.const 4 + i32.const 1 + i32.sub + i32.store offset=4 + local.get $0 + local.tee $1 + i32.const 0 + i32.const 32 + call $~lib/arraybuffer/ArrayBuffer#constructor + local.tee $3 + local.get $1 + i32.load offset=8 + local.tee $2 + i32.ne + if (result i32) + local.get $2 + if + local.get $2 + local.get $1 + call $gc/_dummy/__ref_unlink + end + local.get $3 + local.get $1 + call $gc/_dummy/__ref_link + local.get $3 + else + local.get $3 + end + i32.store offset=8 + local.get $0 + i32.const 4 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $0 + i32.const 0 + i32.store offset=20 + ) + (func $~lib/set/Set#constructor (; 14 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + block (result i32) + local.get $0 + i32.eqz + if + i32.const 24 + call $~lib/runtime/allocate + i32.const 3 + call $~lib/runtime/register + local.set $0 + end + local.get $0 + i32.const 0 + i32.store + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 0 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $0 + i32.const 0 + i32.store offset=20 + local.get $0 + end + call $~lib/set/Set#clear + local.get $0 + ) + (func $~lib/util/hash/hash32 (; 15 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + i32.const -2128831035 + local.set $1 + local.get $1 + local.get $0 + i32.const 255 + i32.and + i32.xor + i32.const 16777619 + i32.mul + local.set $1 + local.get $1 + local.get $0 + i32.const 8 + i32.shr_u + i32.const 255 + i32.and + i32.xor + i32.const 16777619 + i32.mul + local.set $1 + local.get $1 + local.get $0 + i32.const 16 + i32.shr_u + i32.const 255 + i32.and + i32.xor + i32.const 16777619 + i32.mul + local.set $1 + local.get $1 + local.get $0 + i32.const 24 + i32.shr_u + i32.xor + i32.const 16777619 + i32.mul + local.set $1 + local.get $1 + ) + (func $~lib/set/Set#find (; 16 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + local.get $0 + i32.load + local.get $2 + local.get $0 + i32.load offset=4 + i32.and + i32.const 4 + i32.mul + i32.add + i32.load + local.set $3 + block $break|0 + loop $continue|0 + local.get $3 + if + block + local.get $3 + i32.load offset=4 + i32.const 1 + i32.and + i32.eqz + local.tee $4 + if (result i32) + local.get $3 + i32.load + local.get $1 + i32.eq + else + local.get $4 + end + if + local.get $3 + return + end + local.get $3 + i32.load offset=4 + i32.const 1 + i32.const -1 + i32.xor + i32.and + local.set $3 + end + br $continue|0 + end + end + end + i32.const 0 + ) + (func $~lib/set/Set#has (; 17 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $0 + local.get $1 + block $~lib/util/hash/HASH|inlined.0 (result i32) + local.get $1 + local.set $2 + local.get $2 + call $~lib/util/hash/hash32 + br $~lib/util/hash/HASH|inlined.0 + end + call $~lib/set/Set#find + i32.const 0 + i32.ne + ) + (func $~lib/set/Set#rehash (; 18 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + (local $12 i32) + local.get $1 + i32.const 1 + i32.add + local.set $2 + i32.const 0 + local.get $2 + i32.const 4 + i32.mul + call $~lib/arraybuffer/ArrayBuffer#constructor + local.set $3 + local.get $2 + f64.convert_i32_s + f64.const 2.6666666666666665 + f64.mul + i32.trunc_f64_s + local.set $4 + i32.const 0 + local.get $4 + block $~lib/set/ENTRY_SIZE|inlined.1 (result i32) + i32.const 8 + end + i32.mul + call $~lib/arraybuffer/ArrayBuffer#constructor + local.set $5 + local.get $0 + i32.load offset=8 + local.set $6 + local.get $6 + local.get $0 + i32.load offset=16 + block $~lib/set/ENTRY_SIZE|inlined.2 (result i32) + i32.const 8 + end + i32.mul + i32.add + local.set $7 + local.get $5 + local.set $8 + block $break|0 + loop $continue|0 + local.get $6 + local.get $7 + i32.ne + if + block + local.get $6 + local.set $9 + local.get $9 + i32.load offset=4 + i32.const 1 + i32.and + i32.eqz + if + local.get $8 + local.set $10 + local.get $10 + local.get $9 + i32.load + i32.store + block $~lib/util/hash/HASH|inlined.2 (result i32) + local.get $9 + i32.load + local.set $11 + local.get $11 + call $~lib/util/hash/hash32 + br $~lib/util/hash/HASH|inlined.2 + end + local.get $1 + i32.and + local.set $11 + local.get $3 + local.get $11 + i32.const 4 + i32.mul + i32.add + local.set $12 + local.get $10 + local.get $12 + i32.load + i32.store offset=4 + local.get $12 + local.get $8 + i32.store + local.get $8 + block $~lib/set/ENTRY_SIZE|inlined.3 (result i32) + i32.const 8 + end + i32.add + local.set $8 + end + local.get $6 + block $~lib/set/ENTRY_SIZE|inlined.4 (result i32) + i32.const 8 + end + i32.add + local.set $6 + end + br $continue|0 + end + end + end + local.get $0 + local.tee $9 + local.get $3 + local.tee $12 + local.get $9 + i32.load + local.tee $11 + i32.ne + if (result i32) + local.get $11 + if + local.get $11 + local.get $9 + call $gc/_dummy/__ref_unlink + end + local.get $12 + local.get $9 + call $gc/_dummy/__ref_link + local.get $12 + else + local.get $12 + end + i32.store + local.get $0 + local.get $1 + i32.store offset=4 + local.get $0 + local.tee $9 + local.get $5 + local.tee $11 + local.get $9 + i32.load offset=8 + local.tee $12 + i32.ne + if (result i32) + local.get $12 + if + local.get $12 + local.get $9 + call $gc/_dummy/__ref_unlink + end + local.get $11 + local.get $9 + call $gc/_dummy/__ref_link + local.get $11 + else + local.get $11 + end + i32.store offset=8 + local.get $0 + local.get $4 + i32.store offset=12 + local.get $0 + local.get $0 + i32.load offset=20 + i32.store offset=16 + ) + (func $~lib/set/Set#add (; 19 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + block $~lib/util/hash/HASH|inlined.1 (result i32) + local.get $1 + local.set $2 + local.get $2 + call $~lib/util/hash/hash32 + br $~lib/util/hash/HASH|inlined.1 + end + local.set $3 + local.get $0 + local.get $1 + local.get $3 + call $~lib/set/Set#find + local.set $4 + local.get $4 + i32.eqz + if + local.get $0 + i32.load offset=16 + local.get $0 + i32.load offset=12 + i32.eq + if + local.get $0 + local.get $0 + i32.load offset=20 + local.get $0 + i32.load offset=12 + f64.convert_i32_s + f64.const 0.75 + f64.mul + i32.trunc_f64_s + i32.lt_s + if (result i32) + local.get $0 + i32.load offset=4 + else + local.get $0 + i32.load offset=4 + i32.const 1 + i32.shl + i32.const 1 + i32.or + end + call $~lib/set/Set#rehash + end + local.get $0 + i32.load offset=8 + local.set $2 + local.get $2 + block (result i32) + local.get $0 + local.get $0 + i32.load offset=16 + local.tee $5 + i32.const 1 + i32.add + i32.store offset=16 + local.get $5 + end + block $~lib/set/ENTRY_SIZE|inlined.5 (result i32) + i32.const 8 + end + i32.mul + i32.add + local.set $4 + local.get $4 + local.get $1 + i32.store + local.get $0 + local.get $0 + i32.load offset=20 + i32.const 1 + i32.add + i32.store offset=20 + local.get $0 + i32.load + local.get $3 + local.get $0 + i32.load offset=4 + i32.and + i32.const 4 + i32.mul + i32.add + local.set $5 + local.get $4 + local.get $5 + i32.load + i32.store offset=4 + local.get $5 + local.get $4 + i32.store + end + ) + (func $~lib/gc/gc.retain (; 20 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + global.get $~lib/gc/GC_ROOT + local.set $1 + local.get $1 + local.get $0 + call $~lib/set/Set#has + i32.eqz + if + local.get $1 + local.get $0 + call $~lib/set/Set#add + local.get $0 + local.get $1 + call $gc/_dummy/__ref_link + end + ) + (func $~lib/set/Set#delete (; 21 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $0 + local.get $1 + block $~lib/util/hash/HASH|inlined.3 (result i32) + local.get $1 + local.set $2 + local.get $2 + call $~lib/util/hash/hash32 + br $~lib/util/hash/HASH|inlined.3 + end + call $~lib/set/Set#find + local.set $3 + local.get $3 + i32.eqz + if + i32.const 0 + return + end + local.get $3 + local.get $3 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + local.get $0 + local.get $0 + i32.load offset=20 + i32.const 1 + i32.sub + i32.store offset=20 + local.get $0 + i32.load offset=4 + i32.const 1 + i32.shr_u + local.set $4 + local.get $4 + i32.const 1 + i32.add + i32.const 4 + local.tee $2 + local.get $0 + i32.load offset=20 + local.tee $5 + local.get $2 + local.get $5 + i32.gt_u + select + i32.ge_u + local.tee $2 + if (result i32) + local.get $0 + i32.load offset=20 + local.get $0 + i32.load offset=12 + f64.convert_i32_s + f64.const 0.75 + f64.mul + i32.trunc_f64_s + i32.lt_s + else + local.get $2 + end + if + local.get $0 + local.get $4 + call $~lib/set/Set#rehash + end + i32.const 1 + ) + (func $~lib/gc/gc.release (; 22 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + global.get $~lib/gc/GC_ROOT + local.set $1 + local.get $1 + local.get $0 + call $~lib/set/Set#has + if + local.get $1 + local.get $0 + call $~lib/set/Set#delete + drop + local.get $0 + local.get $1 + call $gc/_dummy/__ref_unlink + end + ) + (func $gc/_dummy/__ref_collect (; 23 ;) (type $FUNCSIG$v) + i32.const 272 + i32.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + call $~lib/env/trace + global.get $gc/_dummy/collect_count + i32.const 1 + i32.add + global.set $gc/_dummy/collect_count + ) + (func $~lib/gc/gc.collect (; 24 ;) (type $FUNCSIG$v) + call $gc/_dummy/__ref_collect + ) + (func $gc/main (; 25 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + global.get $~lib/started + i32.eqz + if + call $start + i32.const 1 + global.set $~lib/started + end + i32.const 0 + call $gc/Ref#constructor + local.set $0 + global.get $~lib/gc/gc.implemented + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 10 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/link_count + local.set $1 + global.get $gc/_dummy/unlink_count + local.set $2 + global.get $gc/_dummy/collect_count + local.set $3 + local.get $0 + call $~lib/gc/gc.retain + global.get $gc/_dummy/link_count + local.get $1 + i32.const 1 + i32.add + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 18 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 19 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 20 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/link_count + local.set $1 + global.get $gc/_dummy/unlink_count + local.set $2 + global.get $gc/_dummy/collect_count + local.set $3 + local.get $0 + call $~lib/gc/gc.release + global.get $gc/_dummy/link_count + local.get $1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 27 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $2 + i32.const 1 + i32.add + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 28 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 29 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/link_count + local.set $1 + global.get $gc/_dummy/unlink_count + local.set $2 + global.get $gc/_dummy/collect_count + local.set $3 + call $~lib/gc/gc.collect + global.get $gc/_dummy/link_count + local.get $1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 36 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/unlink_count + local.get $2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 37 + i32.const 2 + call $~lib/env/abort + unreachable + end + global.get $gc/_dummy/collect_count + local.get $3 + i32.const 1 + i32.add + i32.eq + i32.eqz + if + i32.const 0 + i32.const 112 + i32.const 38 + i32.const 2 + call $~lib/env/abort + unreachable + end + ) + (func $start (; 26 ;) (type $FUNCSIG$v) + global.get $~lib/memory/HEAP_BASE + i32.const 7 + i32.add + i32.const 7 + i32.const -1 + i32.xor + i32.and + global.set $~lib/allocator/arena/startOffset + global.get $~lib/allocator/arena/startOffset + global.set $~lib/allocator/arena/offset + i32.const 0 + call $~lib/set/Set#constructor + global.set $~lib/gc/GC_ROOT + ) + (func $null (; 27 ;) (type $FUNCSIG$v) + ) +)