diff --git a/tests/compiler/runtime/asrt.optimized.wat b/tests/compiler/runtime/asrt.optimized.wat index 4f5c7e1a..0b893b00 100644 --- a/tests/compiler/runtime/asrt.optimized.wat +++ b/tests/compiler/runtime/asrt.optimized.wat @@ -1,7 +1,12 @@ (module + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$i (func (result i32))) (type $FUNCSIG$vii (func (param i32 i32))) - (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$viii (func (param i32 i32 i32))) (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$vi (func (param i32))) (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) @@ -9,14 +14,1053 @@ (data (i32.const 24) "r\00u\00n\00t\00i\00m\00e\00/\00a\00s\00r\00t\00.\00t\00s") (data (i32.const 56) "\10\00\00\00\1c") (data (i32.const 72) "~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s") + (global $runtime/asrt/ROOT (mut i32) (i32.const 0)) (global $runtime/asrt/ROOTS (mut i32) (i32.const 0)) (global $runtime/asrt/CUR (mut i32) (i32.const 0)) (export "memory" (memory $0)) + (export "__mm_allocate" (func $runtime/asrt/__mm_allocate)) + (export "__mm_free" (func $runtime/asrt/__mm_free)) (export "__rt_visit" (func $runtime/asrt/__rt_visit)) - (export "retain" (func $runtime/asrt/increment)) - (export "release" (func $runtime/asrt/decrement)) - (export "collect" (func $runtime/asrt/collectCycles)) - (func $runtime/asrt/decrement (; 1 ;) (type $FUNCSIG$vi) (param $0 i32) + (export "__gc_retain" (func $runtime/asrt/__gc_retain)) + (export "__gc_release" (func $runtime/asrt/__gc_release)) + (export "__gc_collect" (func $runtime/asrt/collectCycles)) + (func $runtime/asrt/setSLMap (; 1 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + local.get $1 + i32.const 22 + i32.ge_u + if + i32.const 0 + i32.const 24 + i32.const 167 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 2 + i32.shl + local.get $0 + i32.add + local.get $2 + i32.store offset=4 + ) + (func $runtime/asrt/setHead (; 2 ;) (type $FUNCSIG$viiii) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + local.get $2 + i32.const 32 + i32.lt_u + i32.const 0 + local.get $1 + i32.const 22 + i32.lt_u + select + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 181 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 5 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + local.get $0 + i32.add + local.get $3 + i32.store offset=96 + ) + (func $runtime/asrt/getRight (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.load + local.tee $1 + i32.const -4 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 112 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const -4 + i32.and + i32.add + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 114 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + ) + (func $runtime/asrt/fls (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 467 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + i32.const 31 + local.get $0 + i32.clz + i32.sub + ) + (func $runtime/asrt/getHead (; 5 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + local.get $2 + i32.const 32 + i32.lt_u + i32.const 0 + local.get $1 + i32.const 22 + i32.lt_u + select + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 172 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 5 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + local.get $0 + i32.add + i32.load offset=96 + ) + (func $runtime/asrt/getSLMap (; 6 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + i32.const 22 + i32.ge_u + if + i32.const 0 + i32.const 24 + i32.const 162 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 2 + i32.shl + local.get $0 + i32.add + i32.load offset=4 + ) + (func $runtime/asrt/removeBlock (; 7 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.tee $3 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 262 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const -4 + i32.and + local.tee $2 + i32.const 16 + i32.ge_u + if (result i32) + local.get $2 + i32.const 1073741824 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 264 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 256 + i32.lt_u + if (result i32) + local.get $2 + i32.const 8 + i32.div_u + local.set $4 + i32.const 0 + else + local.get $2 + local.get $2 + call $runtime/asrt/fls + local.tee $3 + i32.const 5 + i32.sub + i32.shr_u + i32.const 32 + i32.xor + local.set $4 + local.get $3 + i32.const 7 + i32.sub + end + local.set $3 + local.get $1 + i32.load offset=20 + local.set $2 + local.get $1 + i32.load offset=16 + local.tee $5 + if + local.get $5 + local.get $2 + i32.store offset=20 + end + local.get $2 + if + local.get $2 + local.get $5 + i32.store offset=16 + end + local.get $0 + local.get $3 + local.get $4 + call $runtime/asrt/getHead + local.get $1 + i32.eq + if + local.get $0 + local.get $3 + local.get $4 + local.get $2 + call $runtime/asrt/setHead + local.get $2 + i32.eqz + if + local.get $0 + local.get $3 + local.get $0 + local.get $3 + call $runtime/asrt/getSLMap + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $1 + call $runtime/asrt/setSLMap + local.get $1 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $3 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + end + end + end + ) + (func $runtime/asrt/getLeft (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.load + i32.const 2 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 103 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 4 + i32.sub + i32.load + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 105 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + ) + (func $runtime/asrt/insertBlock (; 9 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 197 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.tee $3 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 199 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + call $runtime/asrt/getRight + local.tee $5 + i32.load + local.tee $4 + i32.const 1 + i32.and + if + local.get $0 + local.get $5 + call $runtime/asrt/removeBlock + local.get $1 + local.get $4 + i32.const -4 + i32.and + i32.const 16 + i32.add + local.get $3 + i32.add + local.tee $3 + i32.store + local.get $1 + call $runtime/asrt/getRight + local.tee $5 + i32.load + local.set $4 + end + local.get $3 + i32.const 2 + i32.and + if + local.get $1 + call $runtime/asrt/getLeft + local.tee $1 + i32.load + local.tee $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 217 + i32.const 15 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $runtime/asrt/removeBlock + local.get $1 + local.get $3 + i32.const -4 + i32.and + i32.const 16 + i32.add + local.get $2 + i32.add + local.tee $3 + i32.store + end + local.get $5 + local.get $4 + i32.const 2 + i32.or + i32.store + local.get $3 + i32.const -4 + i32.and + local.tee $2 + i32.const 16 + i32.ge_u + if (result i32) + local.get $2 + i32.const 1073741824 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 230 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $5 + local.get $1 + i32.const 16 + i32.add + local.get $2 + i32.add + i32.ne + if + i32.const 0 + i32.const 24 + i32.const 231 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $5 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $0 + local.get $2 + i32.const 256 + i32.lt_u + if (result i32) + local.get $2 + i32.const 8 + i32.div_u + local.set $2 + i32.const 0 + else + local.get $2 + local.get $2 + call $runtime/asrt/fls + local.tee $3 + i32.const 5 + i32.sub + i32.shr_u + i32.const 32 + i32.xor + local.set $2 + local.get $3 + i32.const 7 + i32.sub + end + local.tee $4 + local.get $2 + call $runtime/asrt/getHead + local.set $3 + local.get $1 + i32.const 0 + i32.store offset=16 + local.get $1 + local.get $3 + i32.store offset=20 + local.get $3 + if + local.get $3 + local.get $1 + i32.store offset=16 + end + local.get $0 + local.get $4 + local.get $2 + local.get $1 + call $runtime/asrt/setHead + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.or + i32.store + local.get $0 + local.get $4 + local.get $0 + local.get $4 + call $runtime/asrt/getSLMap + i32.const 1 + local.get $2 + i32.shl + i32.or + call $runtime/asrt/setSLMap + ) + (func $runtime/asrt/addMemory (; 10 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + local.get $2 + i32.const 7 + i32.and + i32.eqz + i32.const 0 + local.get $1 + i32.const 7 + i32.and + i32.eqz + i32.const 0 + local.get $1 + local.get $2 + i32.le_u + select + select + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 374 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=2912 + local.tee $3 + if + local.get $1 + i32.const 16 + i32.sub + local.get $3 + i32.eq + if (result i32) + local.get $3 + i32.load + local.set $4 + local.get $1 + i32.const 16 + i32.sub + else + i32.const 0 + i32.const 24 + i32.const 391 + i32.const 6 + call $~lib/builtins/abort + unreachable + end + local.set $1 + else + local.get $1 + local.get $0 + i32.const 2916 + i32.add + i32.lt_u + if + i32.const 0 + i32.const 24 + i32.const 395 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + local.get $1 + i32.sub + local.tee $2 + i32.const 48 + i32.lt_u + if + return + end + local.get $1 + local.get $4 + i32.const 2 + i32.and + local.get $2 + i32.const 32 + i32.sub + i32.const 1 + i32.or + i32.or + i32.store + local.get $1 + i32.const 0 + i32.store offset=16 + local.get $1 + i32.const 0 + i32.store offset=20 + local.get $1 + local.get $2 + i32.add + i32.const 16 + i32.sub + local.tee $2 + i32.const 2 + i32.store + local.get $0 + local.get $2 + i32.store offset=2912 + local.get $0 + local.get $1 + call $runtime/asrt/insertBlock + ) + (func $runtime/asrt/initialize (; 11 ;) (type $FUNCSIG$i) (result i32) + (local $0 i32) + (local $1 i32) + i32.const 1 + current_memory + local.tee $0 + i32.gt_s + if (result i32) + i32.const 1 + local.get $0 + i32.sub + grow_memory + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + i32.const 104 + i32.const 0 + i32.store + i32.const 3016 + i32.const 0 + i32.store + i32.const 0 + local.set $0 + loop $repeat|0 + local.get $0 + i32.const 22 + i32.lt_u + if + i32.const 104 + local.get $0 + i32.const 0 + call $runtime/asrt/setSLMap + i32.const 0 + local.set $1 + loop $repeat|1 + local.get $1 + i32.const 32 + i32.lt_u + if + i32.const 104 + local.get $0 + local.get $1 + i32.const 0 + call $runtime/asrt/setHead + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $repeat|1 + end + end + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $repeat|0 + end + end + i32.const 104 + i32.const 3024 + current_memory + i32.const 16 + i32.shl + call $runtime/asrt/addMemory + i32.const 104 + ) + (func $runtime/asrt/ffs (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 461 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.ctz + ) + (func $runtime/asrt/searchBlock (; 13 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $1 + i32.const 256 + i32.lt_u + if (result i32) + local.get $1 + i32.const 8 + i32.div_u + else + local.get $1 + call $runtime/asrt/fls + local.tee $3 + i32.const 7 + i32.sub + local.set $2 + local.get $1 + local.get $3 + i32.const 5 + i32.sub + i32.shr_u + i32.const 32 + i32.xor + local.tee $1 + i32.const 31 + i32.lt_u + if (result i32) + local.get $1 + i32.const 1 + i32.add + else + local.get $2 + i32.const 1 + i32.add + local.set $2 + i32.const 0 + end + end + local.set $1 + local.get $0 + local.get $2 + call $runtime/asrt/getSLMap + i32.const -1 + local.get $1 + i32.shl + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $2 + local.get $1 + call $runtime/asrt/ffs + call $runtime/asrt/getHead + else + local.get $0 + i32.load + i32.const -1 + local.get $2 + i32.const 1 + i32.add + i32.shl + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + call $runtime/asrt/ffs + local.tee $1 + call $runtime/asrt/getSLMap + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 331 + i32.const 17 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + local.get $2 + call $runtime/asrt/ffs + call $runtime/asrt/getHead + else + i32.const 0 + end + end + ) + (func $runtime/asrt/growMemory (; 14 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + current_memory + local.tee $2 + local.get $1 + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $2 + local.get $1 + i32.gt_s + select + grow_memory + i32.const 0 + i32.lt_s + if + local.get $1 + grow_memory + i32.const 0 + i32.lt_s + if + unreachable + end + end + local.get $0 + local.get $2 + i32.const 16 + i32.shl + current_memory + i32.const 16 + i32.shl + call $runtime/asrt/addMemory + ) + (func $runtime/asrt/prepareBlock (; 15 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + local.get $1 + i32.load + local.tee $3 + i32.const 1 + i32.and + if (result i32) + local.get $2 + i32.const 7 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 346 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $runtime/asrt/removeBlock + local.get $3 + i32.const -4 + i32.and + local.get $2 + i32.sub + local.tee $4 + i32.const 32 + i32.ge_u + if + local.get $1 + local.get $3 + i32.const 2 + i32.and + local.get $2 + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.get $2 + i32.add + local.tee $2 + local.get $4 + i32.const 16 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $2 + call $runtime/asrt/insertBlock + else + local.get $1 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $1 + call $runtime/asrt/getRight + local.get $1 + call $runtime/asrt/getRight + i32.load + i32.const -3 + i32.and + i32.store + end + local.get $1 + i32.const 16 + i32.add + ) + (func $runtime/asrt/__mm_allocate (; 16 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + global.get $runtime/asrt/ROOT + local.tee $1 + i32.eqz + if + call $runtime/asrt/initialize + local.tee $1 + global.set $runtime/asrt/ROOT + end + local.get $0 + i32.const 1073741824 + i32.gt_u + if + unreachable + end + local.get $1 + local.get $0 + i32.const 7 + i32.add + i32.const -8 + i32.and + local.tee $0 + i32.const 16 + local.get $0 + i32.const 16 + i32.gt_u + select + local.tee $2 + call $runtime/asrt/searchBlock + local.tee $0 + i32.eqz + if + local.get $1 + local.get $2 + call $runtime/asrt/growMemory + local.get $1 + local.get $2 + call $runtime/asrt/searchBlock + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 490 + i32.const 15 + call $~lib/builtins/abort + unreachable + end + end + local.get $0 + i32.load + i32.const -4 + i32.and + local.get $2 + i32.lt_u + if + i32.const 0 + i32.const 24 + i32.const 492 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + local.get $2 + i32.store offset=12 + local.get $1 + local.get $0 + local.get $2 + call $runtime/asrt/prepareBlock + ) + (func $runtime/asrt/freeBlock (; 17 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $1 + i32.load + local.tee $2 + i32.const 1 + i32.and + if + i32.const 0 + i32.const 24 + i32.const 454 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.get $2 + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $1 + call $runtime/asrt/insertBlock + ) + (func $runtime/asrt/__mm_free (; 18 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + local.get $0 + if + local.get $0 + i32.const 7 + i32.and + if + i32.const 0 + i32.const 24 + i32.const 503 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + global.get $runtime/asrt/ROOT + local.tee $1 + if + local.get $1 + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/freeBlock + end + end + ) + (func $runtime/asrt/decrement (; 19 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -34,7 +1078,7 @@ if i32.const 0 i32.const 24 - i32.const 137 + i32.const 642 i32.const 15 call $~lib/builtins/abort unreachable @@ -46,7 +1090,7 @@ end unreachable ) - (func $runtime/asrt/scan (; 2 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/scan (; 20 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -80,7 +1124,7 @@ unreachable end ) - (func $runtime/asrt/collectWhite (; 3 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/collectWhite (; 21 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -98,15 +1142,13 @@ i32.const 0 end if - local.get $0 - local.get $1 - i32.const -1879048193 - i32.and - i32.store offset=4 unreachable end + global.get $runtime/asrt/ROOT + local.get $0 + call $runtime/asrt/freeBlock ) - (func $runtime/asrt/__rt_visit (; 4 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $runtime/asrt/__rt_visit (; 22 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) block $break|0 block $case5|0 block $case4|0 @@ -142,7 +1184,7 @@ if i32.const 0 i32.const 24 - i32.const 94 + i32.const 597 i32.const 17 call $~lib/builtins/abort unreachable @@ -190,7 +1232,7 @@ if i32.const 0 i32.const 24 - i32.const 105 + i32.const 608 i32.const 6 call $~lib/builtins/abort unreachable @@ -220,13 +1262,13 @@ end i32.const 0 i32.const 24 - i32.const 116 + i32.const 619 i32.const 24 call $~lib/builtins/abort unreachable end ) - (func $runtime/asrt/increment (; 5 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/increment (; 23 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -242,7 +1284,7 @@ if i32.const 0 i32.const 24 - i32.const 122 + i32.const 626 i32.const 2 call $~lib/builtins/abort unreachable @@ -253,7 +1295,25 @@ i32.add i32.store offset=4 ) - (func $runtime/asrt/collectCycles (; 6 ;) (type $FUNCSIG$v) + (func $runtime/asrt/__gc_retain (; 24 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/increment + end + ) + (func $runtime/asrt/__gc_release (; 25 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/decrement + end + ) + (func $runtime/asrt/collectCycles (; 26 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -325,7 +1385,9 @@ i32.and select if - unreachable + global.get $runtime/asrt/ROOT + local.get $0 + call $runtime/asrt/freeBlock else local.get $0 local.get $1 @@ -387,7 +1449,7 @@ local.get $4 global.set $runtime/asrt/CUR ) - (func $null (; 7 ;) (type $FUNCSIG$v) + (func $null (; 27 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/runtime/asrt.ts b/tests/compiler/runtime/asrt.ts index ae26e3b8..d33b3cf3 100644 --- a/tests/compiler/runtime/asrt.ts +++ b/tests/compiler/runtime/asrt.ts @@ -1,19 +1,522 @@ -// An experiment on how an ARC runtime could look like. - -// After the paper "A Pure Reference Counting Garbage Collector" by David F. Bacon et al. +// An experimental standalone AssemblyScript runtime based on TLSF and PureRC. // @ts-ignore: decorator @inline const DEBUG = true; +// Alignment guarantees + +// @ts-ignore: decorator +@inline const AL_BITS: u32 = 3; // 8 bytes +// @ts-ignore: decorator +@inline const AL_SIZE: usize = 1 << AL_BITS; +// @ts-ignore: decorator +@inline const AL_MASK: usize = AL_SIZE - 1; + +/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator /////////////////////// +// see: http://www.gii.upv.es/tlsf/ + +// ╒══════════════ Block size interpretation (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 +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─┴─╫─┴─┴─┤ +// │ | FL │ SB = SL + AL │ ◄─ usize +// └───────────────────────────────────────────────┴─────────╨─────┘ +// FL: first level, SL: second level, AL: alignment, SB: small block + +// @ts-ignore: decorator +@inline const SL_BITS: u32 = 5; +// @ts-ignore: decorator +@inline const SL_SIZE: usize = 1 << SL_BITS; + +// @ts-ignore: decorator +@inline const SB_BITS: usize = (SL_BITS + AL_BITS); +// @ts-ignore: decorator +@inline const SB_SIZE: usize = 1 << SB_BITS; + +// @ts-ignore: decorator +@inline +const FL_BITS: u32 = (sizeof() == sizeof() + ? 30 // ^= up to 1GB per block + : 32 // ^= up to 4GB per block +) - SB_BITS; + +// Tags stored in otherwise unused alignment bits + +// @ts-ignore: decorator +@inline const FREE: usize = 1 << 0; +// @ts-ignore: decorator +@inline const LEFTFREE: usize = 1 << 1; +// @ts-ignore: decorator +@inline const TAGS_MASK: usize = FREE | LEFTFREE; // <= AL_MASK + +// ╒════════════════════ Block 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 +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┼─┤ overhead ┐ +// │ size │0│L│F│ ◄─┐ info +// ├─────────────────────────────────────────────────────────┴─┴─┴─┤ │ +// │ │ │ +// │ ... additional runtime overhead ... │ │ +// │ │ │ +// ╞═══════════════════════════════════════════════════════════════╡ │ ┐ ┘ +// │ if free: ◄ prev │ ◄─┤ usize +// ├───────────────────────────────────────────────────────────────┤ │ +// │ if free: next ► │ ◄─┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ ... │ │ = 0 +// ├───────────────────────────────────────────────────────────────┤ │ +// │ if free: back ▲ │ ◄─┘ +// └───────────────────────────────────────────────────────────────┘ payload ┘ >= MIN SIZE +// F: FREE, L: LEFT_FREE +@unmanaged class Block { + + /** Memory manager info. */ + mmInfo: usize; // WASM64 might need adaption + /** Garbage collector info. */ + gcInfo: u32; + /** Runtime class id. */ + rtId: u32; + /** Runtime object size. */ + rtSize: u32; + + /** Previous free block, if any. Only valid if free, otherwise part of payload. */ + prev: Block | null; + /** Next free block, if any. Only valid if free, otherwise part of payload. */ + next: Block | null; + + // If the block is free, there is a backreference at its end pointing at its start. +} + +// Block constants. Overhead is always present, no matter if free or used. Also, a block must have +// a minimum size of three pointers so it can hold `prev`, `next` and `back` if free. + +// @ts-ignore: decorator +@inline const BLOCK_OVERHEAD: usize = (offsetof("prev") + AL_MASK) & ~AL_MASK; +// @ts-ignore: decorator +@inline const BLOCK_MINSIZE: usize = (3 * sizeof() + AL_MASK) & ~AL_MASK;// prev + next + back +// @ts-ignore: decorator +@inline const BLOCK_MAXSIZE: usize = 1 << (FL_BITS + SB_BITS); // 1GB if WASM32, 4GB if WASM64 + +/** Gets the left block of a block. Only valid if the left block is free. */ +function getLeft(block: Block): Block { + if (DEBUG) assert(block.mmInfo & LEFTFREE); // left must be free or it doesn't contain 'back' + var left = load(changetype(block) - sizeof()); + if (DEBUG) assert(left); + return left; +} + +/** Gets the right block of of a block by advancing to the right by its size. */ +function getRight(block: Block): Block { + var mmInfo = block.mmInfo; + if (DEBUG) assert(mmInfo & ~TAGS_MASK); // can't skip beyond the tail block (the only valid empty block) + var right = changetype(changetype(block) + BLOCK_OVERHEAD + (mmInfo & ~TAGS_MASK)); + if (DEBUG) assert(right); + return right; +} + +// ╒════════════════ Root structure 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 +// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐ +// │ 0 | flMap S│ ◄────┐ +// ╞═══════════════════════════════════════════════════════════════╡ │ +// │ slMap[0] S │ ◄─┐ │ +// ├───────────────────────────────────────────────────────────────┤ │ │ +// │ slMap[1] │ ◄─┤ │ +// ├───────────────────────────────────────────────────────────────┤ u32 │ +// │ slMap[22] P │ ◄─┘ │ +// ╞═══════════════════════════════════════════════════════════════╡ usize +// │ head[0] │ ◄────┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ ... │ ◄────┤ +// ├───────────────────────────────────────────────────────────────┤ │ +// │ head[736] │ ◄────┤ +// ╞═══════════════════════════════════════════════════════════════╡ │ +// │ tailRef │ ◄────┘ +// └───────────────────────────────────────────────────────────────┘ SIZE ┘ +@unmanaged class Root { + /** First level bitmap. */ + flMap: usize; +} + +// Root constants. Where stuff is stored inside of the root structure. + +// @ts-ignore: decorator +@inline const SL_START = sizeof(); +// @ts-ignore: decorator +@inline const SL_END = SL_START + (FL_BITS << alignof()); +// ^ +// FIXME: 22 slMaps, what about SB? is it included in 22 or actually 23? + +// @ts-ignore: decorator +@inline const HL_START = (SL_END + AL_MASK) & ~AL_MASK; +// @ts-ignore: decorator +@inline const HL_END = HL_START + FL_BITS * SL_SIZE * sizeof(); +// @ts-ignore: decorator +@inline const ROOT_SIZE = HL_END + sizeof(); + +var ROOT: Root; + +function getSLMap(root: Root, fl: usize): u32 { + if (DEBUG) assert(fl < FL_BITS); // fl out of range + return load(changetype(root) + (fl << alignof()), SL_START); +} + +function setSLMap(root: Root, fl: usize, value: u32): void { + if (DEBUG) assert(fl < FL_BITS); // fl out of range + store(changetype(root) + (fl << alignof()), value, SL_START); +} + +function getHead(root: Root, fl: usize, sl: u32): Block | null { + if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range + return changetype( + load( + changetype(root) + (fl * SL_SIZE + sl) * sizeof(), + HL_START) + ); +} + +function setHead(root: Root, fl: usize, sl: u32, value: Block | null): void { + if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range + store( + changetype(root) + (fl * SL_SIZE + sl) * sizeof() , changetype(value), + HL_START); +} + +function getTail(root: Root): Block { + return load(changetype(root), HL_END); +} + +function setTail(root: Root, tail: Block): void { + store(changetype(root), tail, HL_END); +} + +/** Inserts a previously used block back into the free list. */ +function insertBlock(root: Root, block: Block): void { + if (DEBUG) assert(block); // cannot be null + var blockInfo = block.mmInfo; + if (DEBUG) assert(blockInfo & FREE); // must be free + + var right = getRight(block); + var rightInfo = right.mmInfo; + + // merge with right block if also free + if (rightInfo & FREE) { + removeBlock(root, right); + block.mmInfo = (blockInfo += BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK)); + right = getRight(block); + rightInfo = right.mmInfo; + // 'back' is set below + } + + // merge with left block if also free + if (blockInfo & LEFTFREE) { + let left = getLeft(block); + let leftInfo = left.mmInfo; + if (DEBUG) assert(leftInfo & FREE); // must be free according to right tags + removeBlock(root, left); + left.mmInfo = (leftInfo += BLOCK_OVERHEAD + (blockInfo & ~TAGS_MASK)); + block = left; + blockInfo = leftInfo; + // 'back' is set below + } + + right.mmInfo = rightInfo | LEFTFREE; + // right is no longer used now, hence rightInfo is not synced + + // we now know the size of the block + var size = blockInfo & ~TAGS_MASK; + if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be a valid size + if (DEBUG) assert(changetype(block) + BLOCK_OVERHEAD + size == changetype(right)); // must match + + // set 'back' to itself at the end of block + store(changetype(right) - sizeof(), block); + + // mapping_insert + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + fl = fls(size); + sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + } + + // perform insertion + var head = getHead(root, fl, sl); + block.prev = null; + block.next = head; + if (head) head.prev = block; + setHead(root, fl, sl, block); + + // update first and second level maps + root.flMap |= (1 << fl); + setSLMap(root, fl, getSLMap(root, fl) | (1 << sl)); +} + +/** Removes a free block from internal lists. */ +function removeBlock(root: Root, block: Block): void { + var blockInfo = block.mmInfo; + if (DEBUG) assert(blockInfo & FREE); // must be free + var size = blockInfo & ~TAGS_MASK; + if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be valid + + // mapping_insert + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + fl = fls(size); + sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + } + + // link previous and next free block + var prev = block.prev; + var next = block.next; + if (prev) prev.next = next; + if (next) next.prev = prev; + + // update head if we are removing it + if (block == getHead(root, fl, sl)) { + setHead(root, fl, sl, next); + + // clear second level map if head is empty now + if (!next) { + let slMap = getSLMap(root, fl); + setSLMap(root, fl, slMap &= ~(1 << sl)); + + // clear first level map if second level is empty now + if (!slMap) root.flMap &= ~(1 << fl); + } + } + // note: does not alter left/back because it is likely that splitting + // is performed afterwards, invalidating those changes. so, the caller + // must perform those updates. +} + +/** Searches for a free block of at least the specified size. */ +function searchBlock(root: Root, size: usize): Block | null { + // size was already asserted by caller + + // mapping_search + var fl: usize, sl: u32; + if (size < SB_SIZE) { + fl = 0; + sl = (size / AL_SIZE); + } else { + // (*) size += (1 << (fls(size) - SL_BITS)) - 1; + fl = fls(size); + sl = ((size >> (fl - SL_BITS)) ^ (1 << SL_BITS)); + fl -= SB_BITS - 1; + // (*) instead of rounding up, use next second level list for better fit + if (sl < SL_SIZE - 1) ++sl; + else ++fl, sl = 0; + } + + // search second level + var slMap = getSLMap(root, fl) & (~0 << sl); + var head: Block | null; + if (!slMap) { + // search next larger first level + let flMap = root.flMap & (~0 << (fl + 1)); + if (!flMap) { + head = null; + } else { + fl = ffs(flMap); + slMap = getSLMap(root, fl); + if (DEBUG) assert(slMap); // can't be zero if fl points here + head = getHead(root, fl, ffs(slMap)); + } + } else { + head = getHead(root, fl, ffs(slMap)); + } + return head; +} + +/** Prepares the specified free block for use, possibly splitting it. */ +function prepareBlock(root: Root, block: Block, size: usize): usize { + // size was already asserted by caller + + var blockInfo = block.mmInfo; + if (DEBUG) { + assert( + (blockInfo & FREE) != 0 && // must be free + !(size & AL_MASK) // size must be aligned so the new block is + ); + } + removeBlock(root, block); + + // split if the block can hold another MINSIZE block incl. overhead + var remaining = (blockInfo & ~TAGS_MASK) - size; + if (remaining >= BLOCK_OVERHEAD + BLOCK_MINSIZE) { + block.mmInfo = size | (blockInfo & LEFTFREE); // also discards FREE + + let spare = changetype(changetype(block) + BLOCK_OVERHEAD + size); + spare.mmInfo = (remaining - BLOCK_OVERHEAD) | FREE; // not LEFT_FREE + insertBlock(root, spare); // also sets 'back' + + // otherwise tag block as no longer FREE and right as no longer LEFT_FREE + } else { + block.mmInfo = blockInfo & ~FREE; + getRight(block).mmInfo &= ~LEFTFREE; + } + + return changetype(block) + BLOCK_OVERHEAD; +} + +/** Adds more memory to the pool. */ +function addMemory(root: Root, start: usize, end: usize): bool { + if (DEBUG) { + assert( + start <= end && // must be valid + !(start & AL_MASK) && // must be aligned + !(end & AL_MASK) // must be aligned + ); + } + + var tail = getTail(root); + var tailInfo: usize = 0; + if (tail) { // more memory + // assert(start >= changetype(tail) + BLOCK_OVERHEAD); // starts after tail (zero-sized used block) + + // merge with current tail if adjacent + if (start - BLOCK_OVERHEAD == changetype(tail)) { + start -= BLOCK_OVERHEAD; + tailInfo = tail.mmInfo; + } else if (DEBUG) { + assert(false); // make sure we don't do this, even though possible + } + + } else if (DEBUG) { // first memory + assert(start >= changetype(root) + ROOT_SIZE); // starts after root + } + + // check if size is large enough for a free block and the tail block + var size = end - start; + if (size < BLOCK_OVERHEAD + BLOCK_MINSIZE + BLOCK_OVERHEAD) { + return false; + } + + // left size is total minus its own and the zero-length tail's header + var leftSize = size - 2 * BLOCK_OVERHEAD; + var left = changetype(start); + left.mmInfo = leftSize | FREE | (tailInfo & LEFTFREE); + left.prev = null; + left.next = null; + + // tail is a zero-length used block + tail = changetype(start + size - BLOCK_OVERHEAD); + tail.mmInfo = 0 | LEFTFREE; + setTail(root, tail); + + insertBlock(root, left); // also merges with free left before tail / sets 'back' + + return true; +} + +/** Grows memory to fit at least another block of the specified size. */ +function growMemory(root: Root, size: usize): void { + var pagesBefore = memory.size(); + var pagesNeeded = (((size + 0xffff) & ~0xffff) >>> 16); + var pagesWanted = max(pagesBefore, pagesNeeded); // double memory + if (memory.grow(pagesWanted) < 0) { + if (memory.grow(pagesNeeded) < 0) unreachable(); // out of memory + } + var pagesAfter = memory.size(); + addMemory(root, pagesBefore << 16, pagesAfter << 16); +} + +/** Initilizes the root structure. */ +function initialize(): Root { + var rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK; + var pagesBefore = memory.size(); + var pagesNeeded = ((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16); + if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable(); + var root = changetype(rootOffset); + root.flMap = 0; + setTail(root, changetype(0)); + for (let fl: usize = 0; fl < FL_BITS; ++fl) { + setSLMap(root, fl, 0); + for (let sl: u32 = 0; sl < SL_SIZE; ++sl) { + setHead(root, fl, sl, null); + } + } + addMemory(root, (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16); + return root; +} + +function freeBlock(root: Root, block: Block): void { + var blockInfo = block.mmInfo; + if (DEBUG) assert(!(blockInfo & FREE)); // must be used + block.mmInfo = blockInfo | FREE; + insertBlock(root, block); +} + +/** Determines the first (LSB to MSB) set bit's index of a word. */ +function ffs(word: T): T { + if (DEBUG) assert(word != 0); // word cannot be 0 + return ctz(word); // differs from ffs only for 0 +} + +/** Determines the last (LSB to MSB) set bit's index of a word. */ +function fls(word: T): T { + if (DEBUG) assert(word != 0); // word cannot be 0 + // @ts-ignore: type + const inv: T = (sizeof() << 3) - 1; + // @ts-ignore: type + return inv - clz(word); +} + +// Memory manager interface. + +// @ts-ignore: decorator +@global @unsafe +function __mm_allocate(size: usize): usize { + // initialize if necessary + var root = ROOT; + if (!root) ROOT = root = initialize(); + + // search for a suitable block + if (size > BLOCK_MAXSIZE) unreachable(); + size = max((size + AL_MASK) & ~AL_MASK, BLOCK_MINSIZE); // valid + var block = searchBlock(root, size); + if (!block) { + growMemory(root, size); + block = searchBlock(root, size); + if (DEBUG) assert(block); // must be found now + } + if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= size); // must fit + block.gcInfo = 0; + block.rtId = 0; // not determined yet + block.rtSize = size; + return prepareBlock(root, block, size); // FIXME: why's still necessary? +} + +// @ts-ignore: decorator +@global @unsafe +function __mm_free(data: usize): void { + if (data) { + assert(!(data & AL_MASK)); // must be aligned + let root = ROOT; + if (root) freeBlock(root, changetype(data - BLOCK_OVERHEAD)); + } +} + +/////////////////////////// A Pure Reference Counting Garbage Collector /////////////////////////// +// see: https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon03Pure.pdf + // TODO: make visitors eat cookies so we can compile direct calls into a switch function __rt_visit_members(s: Block, cookie: i32): void { unreachable(); } function __rt_flags(classId: u32): u32 { return unreachable(); } const ACYCLIC_FLAG: u32 = 0; // ╒══════════════════════ GC Info structure ══════════════════════╕ -// 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 +// │ 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│ // ├─┼─┴─┴─┼─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ // │B│color│ refCount │ // └─┴─────┴───────────────────────────────────────────────────────┘ @@ -21,30 +524,30 @@ const ACYCLIC_FLAG: u32 = 0; // @ts-ignore: decorator @inline -const BUFFERED_BIT: u32 = 1 << (sizeof() * 8 - 1); +const BUFFERED_MASK: u32 = 1 << (sizeof() * 8 - 1); // @ts-ignore: decorator @inline -const COLOR_SIZE = 3; +const COLOR_BITS = 3; // @ts-ignore: decorator @inline -const COLOR_SHIFT: u32 = ctz(BUFFERED_BIT) - COLOR_SIZE; +const COLOR_SHIFT: u32 = ctz(BUFFERED_MASK) - COLOR_BITS; // @ts-ignore: decorator @inline -const COLOR_BITS: u32 = ((1 << COLOR_SIZE) - 1) << COLOR_SHIFT; +const COLOR_MASK: u32 = ((1 << COLOR_BITS) - 1) << COLOR_SHIFT; // @ts-ignore: decorator @inline -const REFCOUNT_BITS: u32 = (1 << COLOR_SHIFT) - 1; +const REFCOUNT_MASK: u32 = (1 << COLOR_SHIFT) - 1; -// ╒════════╤════════════ Colors ══════════════════════╕ -// │ Color │ Meaning │ -// ├────────┼──────────────────────────────────────────┤ -// │ BLACK │ In use or free │ -// │ GRAY │ Possible member of cycle │ -// │ WHITE │ Member of garbage cycle │ -// │ PURPLE │ Possible root of cycle │ -// │ RED │ Candidate cycle undergoing Σ-computation │ concurrent only -// │ ORANGE │ Candidate cycle awaiting epoch boundary │ concurrent only -// └────────┴──────────────────────────────────────────┘ +// ╒════════╤═══════════════════ Colors ═══════════════════════════╕ +// │ Color │ Meaning │ +// ├────────┼──────────────────────────────────────────────────────┤ +// │ BLACK │ In use or free │ +// │ GRAY │ Possible member of cycle │ +// │ WHITE │ Member of garbage cycle │ +// │ PURPLE │ Possible root of cycle │ +// │ RED │ Candidate cycle undergoing Σ-computation *concurrent │ +// │ ORANGE │ Candidate cycle awaiting epoch boundary *concurrent │ +// └────────┴──────────────────────────────────────────────────────┘ // Acyclic detection has been decoupled, hence no GREEN. // @ts-ignore: decorator @@ -91,7 +594,7 @@ function __rt_visit(s: Block, cookie: i32): void { break; } case VISIT_MARKGRAY: { - if (DEBUG) assert((s.gcInfo & REFCOUNT_BITS) > 0); + if (DEBUG) assert((s.gcInfo & REFCOUNT_MASK) > 0); s.gcInfo = s.gcInfo - 1; markGray(s); break; @@ -102,9 +605,9 @@ function __rt_visit(s: Block, cookie: i32): void { } case VISIT_SCANBLACK: { let info = s.gcInfo; - assert((info & ~REFCOUNT_BITS) == ((info + 1) & ~REFCOUNT_BITS)); // overflow + assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow s.gcInfo = info + 1; - if ((info & COLOR_BITS) != COLOR_BLACK) { + if ((info & COLOR_MASK) != COLOR_BLACK) { scanBlack(s); } break; @@ -117,39 +620,45 @@ function __rt_visit(s: Block, cookie: i32): void { } } +/** Increments the reference count of the specified block by one.*/ function increment(s: Block): void { var info = s.gcInfo; - assert((info & ~REFCOUNT_BITS) == ((info + 1) & ~REFCOUNT_BITS)); // overflow + assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow s.gcInfo = info + 1; } +/** Decrements the reference count of the specified block by one, possibly freeing it. */ function decrement(s: Block): void { var info = s.gcInfo; - var rc = info & REFCOUNT_BITS; + var rc = info & REFCOUNT_MASK; if (rc == 1) { __rt_visit_members(s, VISIT_DECREMENT); - if (!(info & BUFFERED_BIT)) { - free(s); + if (!(info & BUFFERED_MASK)) { + freeBlock(ROOT, s); } else { - s.gcInfo = BUFFERED_BIT | COLOR_BLACK | 0; + s.gcInfo = BUFFERED_MASK | COLOR_BLACK | 0; } } else { if (DEBUG) assert(rc > 0); - if (!(__rt_flags(s.classId) & ACYCLIC_FLAG)) { - s.gcInfo = BUFFERED_BIT | COLOR_PURPLE | (rc - 1); - if (!(info & BUFFERED_BIT)) { + if (!(__rt_flags(s.rtId) & ACYCLIC_FLAG)) { + s.gcInfo = BUFFERED_MASK | COLOR_PURPLE | (rc - 1); + if (!(info & BUFFERED_MASK)) { appendRoot(s); } } else { - s.gcInfo = (info & ~REFCOUNT_BITS) | (rc - 1); + s.gcInfo = (info & ~REFCOUNT_MASK) | (rc - 1); } } } +/** Buffer of possible roots. */ var ROOTS: usize; +/** Current absolute offset into the `ROOTS` buffer. */ var CUR: usize = 0; +/** Current absolute end offset into the `ROOTS` buffer. */ var END: usize = 0; +/** Appends a block to possible roots. */ function appendRoot(s: Block): void { var cur = CUR; if (cur >= END) { @@ -160,6 +669,7 @@ function appendRoot(s: Block): void { CUR = cur + 1; } +/** Grows the roots buffer if it ran full. */ function growRoots(): void { var oldRoots = ROOTS; var oldSize = CUR - oldRoots; @@ -171,6 +681,7 @@ function growRoots(): void { END = newRoots + newSize; } +/** Collects cyclic garbage. */ function collectCycles(): void { // markRoots @@ -179,15 +690,15 @@ function collectCycles(): void { for (let pos = cur, end = CUR; pos < end; pos += sizeof()) { let s = load(pos); let info = s.gcInfo; - if ((info & COLOR_BITS) == COLOR_PURPLE && (info & REFCOUNT_BITS) > 0) { + if ((info & COLOR_MASK) == COLOR_PURPLE && (info & REFCOUNT_MASK) > 0) { markGray(s); store(cur, s); cur += sizeof(); } else { - if ((info & COLOR_BITS) == COLOR_BLACK && !(info & REFCOUNT_BITS)) { - free(s); + if ((info & COLOR_MASK) == COLOR_BLACK && !(info & REFCOUNT_MASK)) { + freeBlock(ROOT, s); } else { - s.gcInfo = info & ~BUFFERED_BIT; + s.gcInfo = info & ~BUFFERED_MASK; } } } @@ -201,66 +712,70 @@ function collectCycles(): void { // collectRoots for (let pos = roots; pos < cur; pos += sizeof()) { let s = load(pos); - s.gcInfo = s.gcInfo & ~BUFFERED_BIT; + s.gcInfo = s.gcInfo & ~BUFFERED_MASK; collectWhite(s); } CUR = roots; } +/** Marks a block as gray (possible member of cycle) during the collection phase. */ function markGray(s: Block): void { var info = s.gcInfo; - if ((info & COLOR_BITS) != COLOR_GRAY) { - s.gcInfo = (info & ~COLOR_BITS) | COLOR_GRAY; + if ((info & COLOR_MASK) != COLOR_GRAY) { + s.gcInfo = (info & ~COLOR_MASK) | COLOR_GRAY; __rt_visit_members(s, VISIT_MARKGRAY); } } +/** Scans a block during the collection phase, determining whether it is garbage or not. */ function scan(s: Block): void { var info = s.gcInfo; - if ((info & COLOR_BITS) == COLOR_GRAY) { - if ((info & REFCOUNT_BITS) > 0) { + if ((info & COLOR_MASK) == COLOR_GRAY) { + if ((info & REFCOUNT_MASK) > 0) { scanBlack(s); } else { - s.gcInfo = (info & ~COLOR_BITS) | COLOR_WHITE; + s.gcInfo = (info & ~COLOR_MASK) | COLOR_WHITE; __rt_visit_members(s, VISIT_SCAN); } } } +/** Marks a block as black (in use) if it was found to be reachable during the collection phase. */ function scanBlack(s: Block): void { - s.gcInfo = (s.gcInfo & ~COLOR_BITS) | COLOR_BLACK; + s.gcInfo = (s.gcInfo & ~COLOR_MASK) | COLOR_BLACK; __rt_visit_members(s, VISIT_SCANBLACK); } +/** Collects all white (member of a garbage cycle) nodes when completing the collection phase. */ function collectWhite(s: Block): void { var info = s.gcInfo; - if ((info & COLOR_BITS) == COLOR_WHITE && !(info & BUFFERED_BIT)) { - s.gcInfo = (info & ~COLOR_BITS) | COLOR_BLACK; + if ((info & COLOR_MASK) == COLOR_WHITE && !(info & BUFFERED_MASK)) { + // s.gcInfo = (info & ~COLOR_MASK) | COLOR_BLACK; __rt_visit_members(s, VISIT_COLLECTWHITE); } + freeBlock(ROOT, s); } -function free(s: Block): void { - unreachable(); // TODO +// Garbage collector interface + +// @ts-ignore: decorator +@global @unsafe +function __gc_retain(ref: usize): void { + if (ref) increment(changetype(ref - BLOCK_OVERHEAD)); } -// TODO: merge with TLSF -@unmanaged -class Block { - /** Memory manager info. */ - mmInfo: usize; // u32 in WASM32. WASM64 might need adaption - /** Garbage collector info. */ - gcInfo: u32; - /** Runtime class id. */ - classId: u32; - /** Runtime object payload size. */ - payloadSize: u32; +// @ts-ignore: decorator +@global @unsafe +function __gc_release(ref: usize): void { + if (ref) decrement(changetype(ref - BLOCK_OVERHEAD)); } // keep alive, everything else is reached from here export { + __mm_allocate, + __mm_free, __rt_visit, - increment as retain, - decrement as release, - collectCycles as collect + __gc_retain, + __gc_release, + collectCycles as __gc_collect }; diff --git a/tests/compiler/runtime/asrt.untouched.wat b/tests/compiler/runtime/asrt.untouched.wat index 5451f067..9a8638e2 100644 --- a/tests/compiler/runtime/asrt.untouched.wat +++ b/tests/compiler/runtime/asrt.untouched.wat @@ -1,35 +1,1319 @@ (module - (type $FUNCSIG$vii (func (param i32 i32))) - (type $FUNCSIG$vi (func (param i32))) - (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$ii (func (param i32) (result i32))) - (type $FUNCSIG$v (func)) + (type $FUNCSIG$i (func (result i32))) + (type $FUNCSIG$vii (func (param i32 i32))) (type $FUNCSIG$viii (func (param i32 i32 i32))) + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) (data (i32.const 8) "\10\00\00\00\1e\00\00\00\00\00\00\00\00\00\00\00r\00u\00n\00t\00i\00m\00e\00/\00a\00s\00r\00t\00.\00t\00s\00") (data (i32.const 56) "\10\00\00\00\1c\00\00\00\00\00\00\00\00\00\00\00~\00l\00i\00b\00/\00m\00e\00m\00o\00r\00y\00.\00t\00s\00") (table $0 1 funcref) (elem (i32.const 0) $null) + (global $runtime/asrt/ROOT (mut i32) (i32.const 0)) (global $runtime/asrt/ACYCLIC_FLAG i32 (i32.const 0)) (global $runtime/asrt/ROOTS (mut i32) (i32.const 0)) (global $runtime/asrt/CUR (mut i32) (i32.const 0)) (global $runtime/asrt/END (mut i32) (i32.const 0)) + (global $~lib/memory/HEAP_BASE i32 (i32.const 100)) (export "memory" (memory $0)) + (export "__mm_allocate" (func $runtime/asrt/__mm_allocate)) + (export "__mm_free" (func $runtime/asrt/__mm_free)) (export "__rt_visit" (func $runtime/asrt/__rt_visit)) - (export "retain" (func $runtime/asrt/increment)) - (export "release" (func $runtime/asrt/decrement)) - (export "collect" (func $runtime/asrt/collectCycles)) - (func $runtime/asrt/__rt_visit_members (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (export "__gc_retain" (func $runtime/asrt/__gc_retain)) + (export "__gc_release" (func $runtime/asrt/__gc_release)) + (export "__gc_collect" (func $runtime/asrt/collectCycles)) + (func $runtime/asrt/setTail (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=2912 + ) + (func $runtime/asrt/setSLMap (; 2 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + local.get $1 + i32.const 22 + i32.lt_u + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 167 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 2 + i32.shl + i32.add + local.get $2 + i32.store offset=4 + ) + (func $runtime/asrt/setHead (; 3 ;) (type $FUNCSIG$viiii) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + local.get $1 + i32.const 22 + i32.lt_u + if (result i32) + local.get $2 + i32.const 32 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 181 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 32 + i32.mul + local.get $2 + i32.add + i32.const 4 + i32.mul + i32.add + local.get $3 + i32.store offset=96 + ) + (func $runtime/asrt/getTail (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.load offset=2912 + ) + (func $runtime/asrt/getRight (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + local.get $0 + i32.load + local.set $1 + local.get $1 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 112 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 16 + i32.add + local.get $1 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $2 + local.get $2 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 114 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $2 + ) + (func $runtime/asrt/fls (; 6 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 467 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + i32.const 31 + local.get $0 + i32.clz + i32.sub + ) + (func $runtime/asrt/getHead (; 7 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + local.get $1 + i32.const 22 + i32.lt_u + if (result i32) + local.get $2 + i32.const 32 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 172 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 32 + i32.mul + local.get $2 + i32.add + i32.const 4 + i32.mul + i32.add + i32.load offset=96 + ) + (func $runtime/asrt/getSLMap (; 8 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + local.get $1 + i32.const 22 + i32.lt_u + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 162 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + ) + (func $runtime/asrt/removeBlock (; 9 ;) (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.load + local.set $2 + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 262 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $3 + local.get $3 + i32.const 16 + i32.ge_u + if (result i32) + local.get $3 + i32.const 1073741824 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 264 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $4 + local.get $3 + i32.const 8 + i32.div_u + local.set $5 + else + local.get $3 + call $runtime/asrt/fls + local.set $4 + local.get $3 + local.get $4 + i32.const 5 + i32.sub + i32.shr_u + i32.const 1 + i32.const 5 + i32.shl + i32.xor + local.set $5 + local.get $4 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $4 + end + local.get $1 + i32.load offset=16 + local.set $6 + local.get $1 + i32.load offset=20 + local.set $7 + local.get $6 + if + local.get $6 + local.get $7 + i32.store offset=20 + end + local.get $7 + if + local.get $7 + local.get $6 + i32.store offset=16 + end + local.get $1 + local.get $0 + local.get $4 + local.get $5 + call $runtime/asrt/getHead + i32.eq + if + local.get $0 + local.get $4 + local.get $5 + local.get $7 + call $runtime/asrt/setHead + local.get $7 + i32.eqz + if + local.get $0 + local.get $4 + call $runtime/asrt/getSLMap + local.set $8 + local.get $0 + local.get $4 + local.get $8 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $8 + call $runtime/asrt/setSLMap + local.get $8 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + end + end + end + ) + (func $runtime/asrt/getLeft (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.load + i32.const 2 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 103 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 4 + i32.sub + i32.load + local.set $1 + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 105 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + ) + (func $runtime/asrt/insertBlock (; 11 ;) (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.get $1 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 197 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.set $2 + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 199 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + call $runtime/asrt/getRight + local.set $3 + local.get $3 + i32.load + local.set $4 + local.get $4 + i32.const 1 + i32.and + if + local.get $0 + local.get $3 + call $runtime/asrt/removeBlock + local.get $1 + local.get $2 + i32.const 16 + local.get $4 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + i32.add + local.tee $2 + i32.store + local.get $1 + call $runtime/asrt/getRight + local.set $3 + local.get $3 + i32.load + local.set $4 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $1 + call $runtime/asrt/getLeft + local.set $5 + local.get $5 + i32.load + local.set $6 + local.get $6 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 217 + i32.const 15 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $5 + call $runtime/asrt/removeBlock + local.get $5 + local.get $6 + i32.const 16 + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + i32.add + local.tee $6 + i32.store + local.get $5 + local.set $1 + local.get $6 + local.set $2 + end + local.get $3 + local.get $4 + i32.const 2 + i32.or + i32.store + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $7 + local.get $7 + i32.const 16 + i32.ge_u + if (result i32) + local.get $7 + i32.const 1073741824 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 230 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 16 + i32.add + local.get $7 + i32.add + local.get $3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 231 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $7 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $8 + local.get $7 + i32.const 8 + i32.div_u + local.set $9 + else + local.get $7 + call $runtime/asrt/fls + local.set $8 + local.get $7 + local.get $8 + i32.const 5 + i32.sub + i32.shr_u + i32.const 1 + i32.const 5 + i32.shl + i32.xor + local.set $9 + local.get $8 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $8 + end + local.get $0 + local.get $8 + local.get $9 + call $runtime/asrt/getHead + local.set $10 + local.get $1 + i32.const 0 + i32.store offset=16 + local.get $1 + local.get $10 + i32.store offset=20 + local.get $10 + if + local.get $10 + local.get $1 + i32.store offset=16 + end + local.get $0 + local.get $8 + local.get $9 + local.get $1 + call $runtime/asrt/setHead + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $8 + i32.shl + i32.or + i32.store + local.get $0 + local.get $8 + local.get $0 + local.get $8 + call $runtime/asrt/getSLMap + i32.const 1 + local.get $9 + i32.shl + i32.or + call $runtime/asrt/setSLMap + ) + (func $runtime/asrt/addMemory (; 12 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + local.get $1 + local.get $2 + i32.le_u + if (result i32) + local.get $1 + i32.const 7 + i32.and + i32.eqz + else + i32.const 0 + end + if (result i32) + local.get $2 + i32.const 7 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 374 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + local.get $0 + call $runtime/asrt/getTail + local.set $3 + i32.const 0 + local.set $4 + local.get $3 + if + local.get $1 + i32.const 16 + i32.sub + local.get $3 + i32.eq + if + local.get $1 + i32.const 16 + i32.sub + local.set $1 + local.get $3 + i32.load + local.set $4 + else + i32.const 0 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 391 + i32.const 6 + call $~lib/builtins/abort + unreachable + end + end + else + local.get $1 + local.get $0 + i32.const 2916 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 395 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + local.get $1 + i32.sub + local.set $5 + local.get $5 + i32.const 16 + i32.const 16 + i32.add + i32.const 16 + i32.add + i32.lt_u + if + i32.const 0 + return + end + local.get $5 + i32.const 2 + i32.const 16 + i32.mul + i32.sub + local.set $6 + local.get $1 + local.set $7 + local.get $7 + local.get $6 + i32.const 1 + i32.or + local.get $4 + i32.const 2 + i32.and + i32.or + i32.store + local.get $7 + i32.const 0 + i32.store offset=16 + local.get $7 + i32.const 0 + i32.store offset=20 + local.get $1 + local.get $5 + i32.add + i32.const 16 + i32.sub + local.set $3 + local.get $3 + i32.const 0 + i32.const 2 + i32.or + i32.store + local.get $0 + local.get $3 + call $runtime/asrt/setTail + local.get $0 + local.get $7 + call $runtime/asrt/insertBlock + i32.const 1 + ) + (func $runtime/asrt/initialize (; 13 ;) (type $FUNCSIG$i) (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + global.get $~lib/memory/HEAP_BASE + i32.const 7 + i32.add + i32.const 7 + i32.const -1 + i32.xor + i32.and + local.set $0 + current_memory + local.set $1 + local.get $0 + i32.const 2916 + i32.add + 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 $2 + local.get $1 + i32.gt_s + if (result i32) + local.get $2 + local.get $1 + i32.sub + grow_memory + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + local.get $0 + local.set $3 + local.get $3 + i32.const 0 + i32.store + local.get $3 + i32.const 0 + call $runtime/asrt/setTail + block $break|0 + i32.const 0 + local.set $4 + loop $repeat|0 + local.get $4 + i32.const 22 + i32.lt_u + i32.eqz + br_if $break|0 + block + local.get $3 + local.get $4 + i32.const 0 + call $runtime/asrt/setSLMap + block $break|1 + i32.const 0 + local.set $5 + loop $repeat|1 + local.get $5 + i32.const 32 + i32.lt_u + i32.eqz + br_if $break|1 + local.get $3 + local.get $4 + local.get $5 + i32.const 0 + call $runtime/asrt/setHead + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $repeat|1 + unreachable + end + unreachable + end + end + local.get $4 + i32.const 1 + i32.add + local.set $4 + br $repeat|0 + unreachable + end + unreachable + end + local.get $3 + local.get $0 + i32.const 2916 + i32.add + i32.const 7 + i32.add + i32.const 7 + i32.const -1 + i32.xor + i32.and + current_memory + i32.const 16 + i32.shl + call $runtime/asrt/addMemory + drop + local.get $3 + ) + (func $runtime/asrt/ffs (; 14 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 461 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.ctz + ) + (func $runtime/asrt/ffs (; 15 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 461 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.ctz + ) + (func $runtime/asrt/searchBlock (; 16 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $1 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $2 + local.get $1 + i32.const 8 + i32.div_u + local.set $3 + else + local.get $1 + call $runtime/asrt/fls + local.set $2 + local.get $1 + local.get $2 + i32.const 5 + i32.sub + i32.shr_u + i32.const 1 + i32.const 5 + i32.shl + i32.xor + local.set $3 + local.get $2 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $2 + local.get $3 + i32.const 32 + i32.const 1 + i32.sub + i32.lt_u + if + local.get $3 + i32.const 1 + i32.add + local.set $3 + else + local.get $2 + i32.const 1 + i32.add + local.set $2 + i32.const 0 + local.set $3 + end + end + local.get $0 + local.get $2 + call $runtime/asrt/getSLMap + i32.const 0 + i32.const -1 + i32.xor + local.get $3 + i32.shl + i32.and + local.set $4 + local.get $4 + i32.eqz + if + local.get $0 + i32.load + i32.const 0 + i32.const -1 + i32.xor + local.get $2 + i32.const 1 + i32.add + i32.shl + i32.and + local.set $6 + local.get $6 + i32.eqz + if + i32.const 0 + local.set $5 + else + local.get $6 + call $runtime/asrt/ffs + local.set $2 + local.get $0 + local.get $2 + call $runtime/asrt/getSLMap + local.set $4 + local.get $4 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 331 + i32.const 17 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + local.get $4 + call $runtime/asrt/ffs + call $runtime/asrt/getHead + local.set $5 + end + else + local.get $0 + local.get $2 + local.get $4 + call $runtime/asrt/ffs + call $runtime/asrt/getHead + local.set $5 + end + local.get $5 + ) + (func $runtime/asrt/growMemory (; 17 ;) (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) + current_memory + local.set $2 + local.get $1 + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $2 + local.tee $4 + local.get $3 + local.tee $5 + local.get $4 + local.get $5 + i32.gt_s + select + local.set $6 + local.get $6 + grow_memory + i32.const 0 + i32.lt_s + if + local.get $3 + grow_memory + i32.const 0 + i32.lt_s + if + unreachable + end + end + current_memory + local.set $7 + local.get $0 + local.get $2 + i32.const 16 + i32.shl + local.get $7 + i32.const 16 + i32.shl + call $runtime/asrt/addMemory + drop + ) + (func $runtime/asrt/prepareBlock (; 18 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.set $3 + local.get $3 + i32.const 1 + i32.and + i32.const 0 + i32.ne + if (result i32) + local.get $2 + i32.const 7 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 346 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $runtime/asrt/removeBlock + local.get $3 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.sub + local.set $4 + local.get $4 + i32.const 16 + i32.const 16 + i32.add + i32.ge_u + if + local.get $1 + local.get $2 + local.get $3 + i32.const 2 + i32.and + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.get $2 + i32.add + local.set $5 + local.get $5 + local.get $4 + i32.const 16 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $5 + call $runtime/asrt/insertBlock + else + local.get $1 + local.get $3 + i32.const 1 + i32.const -1 + i32.xor + i32.and + i32.store + local.get $1 + call $runtime/asrt/getRight + local.get $1 + call $runtime/asrt/getRight + i32.load + i32.const 2 + i32.const -1 + i32.xor + i32.and + i32.store + end + local.get $1 + i32.const 16 + i32.add + ) + (func $runtime/asrt/__mm_allocate (; 19 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + global.get $runtime/asrt/ROOT + local.set $1 + local.get $1 + i32.eqz + if + call $runtime/asrt/initialize + local.tee $1 + global.set $runtime/asrt/ROOT + end + local.get $0 + i32.const 1073741824 + i32.gt_u + if + unreachable + end + local.get $0 + i32.const 7 + i32.add + i32.const 7 + i32.const -1 + i32.xor + i32.and + local.tee $2 + i32.const 16 + local.tee $3 + local.get $2 + local.get $3 + i32.gt_u + select + local.set $0 + local.get $1 + local.get $0 + call $runtime/asrt/searchBlock + local.set $4 + local.get $4 + i32.eqz + if + local.get $1 + local.get $0 + call $runtime/asrt/growMemory + local.get $1 + local.get $0 + call $runtime/asrt/searchBlock + local.set $4 + local.get $4 + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 490 + i32.const 15 + call $~lib/builtins/abort + unreachable + end + end + local.get $4 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $0 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 492 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $4 + i32.const 0 + i32.store offset=4 + local.get $4 + i32.const 0 + i32.store offset=8 + local.get $4 + local.get $0 + i32.store offset=12 + local.get $1 + local.get $4 + local.get $0 + call $runtime/asrt/prepareBlock + ) + (func $runtime/asrt/freeBlock (; 20 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $1 + i32.load + local.set $2 + local.get $2 + i32.const 1 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 454 + i32.const 13 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.get $2 + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $1 + call $runtime/asrt/insertBlock + ) + (func $runtime/asrt/__mm_free (; 21 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + local.get $0 + if + local.get $0 + i32.const 7 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 503 + i32.const 4 + call $~lib/builtins/abort + unreachable + end + global.get $runtime/asrt/ROOT + local.set $1 + local.get $1 + if + local.get $1 + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/freeBlock + end + end + ) + (func $runtime/asrt/__rt_visit_members (; 22 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) unreachable ) - (func $runtime/asrt/free (; 2 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/__rt_flags (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) unreachable ) - (func $runtime/asrt/__rt_flags (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - unreachable - ) - (func $~lib/memory/memory.allocate (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/memory/memory.allocate (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) i32.const 0 i32.const 72 i32.const 61 @@ -37,7 +1321,7 @@ call $~lib/builtins/abort unreachable ) - (func $~lib/memory/memory.copy (; 5 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/memory/memory.copy (; 25 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -246,7 +1530,7 @@ end end ) - (func $runtime/asrt/growRoots (; 6 ;) (type $FUNCSIG$v) + (func $runtime/asrt/growRoots (; 26 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -290,7 +1574,7 @@ i32.add global.set $runtime/asrt/END ) - (func $runtime/asrt/appendRoot (; 7 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/appendRoot (; 27 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) global.get $runtime/asrt/CUR local.set $1 @@ -310,7 +1594,7 @@ i32.add global.set $runtime/asrt/CUR ) - (func $runtime/asrt/decrement (; 8 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/decrement (; 28 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) (local $2 i32) local.get $0 @@ -332,8 +1616,9 @@ i32.and i32.eqz if + global.get $runtime/asrt/ROOT local.get $0 - call $runtime/asrt/free + call $runtime/asrt/freeBlock else local.get $0 i32.const -2147483648 @@ -351,7 +1636,7 @@ if i32.const 0 i32.const 24 - i32.const 137 + i32.const 642 i32.const 15 call $~lib/builtins/abort unreachable @@ -395,7 +1680,7 @@ end end ) - (func $runtime/asrt/markGray (; 9 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/markGray (; 29 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -420,7 +1705,7 @@ call $runtime/asrt/__rt_visit_members end ) - (func $runtime/asrt/scanBlack (; 10 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/scanBlack (; 30 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 local.get $0 i32.load offset=4 @@ -435,7 +1720,7 @@ i32.const 4 call $runtime/asrt/__rt_visit_members ) - (func $runtime/asrt/scan (; 11 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/scan (; 31 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -470,7 +1755,7 @@ end end ) - (func $runtime/asrt/collectWhite (; 12 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/collectWhite (; 32 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -489,21 +1774,15 @@ i32.const 0 end if - local.get $0 - local.get $1 - i32.const 1879048192 - i32.const -1 - i32.xor - i32.and - i32.const 0 - i32.or - i32.store offset=4 local.get $0 i32.const 5 call $runtime/asrt/__rt_visit_members end + global.get $runtime/asrt/ROOT + local.get $0 + call $runtime/asrt/freeBlock ) - (func $runtime/asrt/__rt_visit (; 13 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $runtime/asrt/__rt_visit (; 33 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) block $break|0 block $case5|0 @@ -555,7 +1834,7 @@ if i32.const 0 i32.const 24 - i32.const 94 + i32.const 597 i32.const 17 call $~lib/builtins/abort unreachable @@ -602,7 +1881,7 @@ if i32.const 0 i32.const 24 - i32.const 105 + i32.const 608 i32.const 6 call $~lib/builtins/abort unreachable @@ -639,14 +1918,14 @@ if i32.const 0 i32.const 24 - i32.const 116 + i32.const 619 i32.const 24 call $~lib/builtins/abort unreachable end end ) - (func $runtime/asrt/increment (; 14 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $runtime/asrt/increment (; 34 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) local.get $0 i32.load offset=4 @@ -668,7 +1947,7 @@ if i32.const 0 i32.const 24 - i32.const 122 + i32.const 626 i32.const 2 call $~lib/builtins/abort unreachable @@ -679,7 +1958,25 @@ i32.add i32.store offset=4 ) - (func $runtime/asrt/collectCycles (; 15 ;) (type $FUNCSIG$v) + (func $runtime/asrt/__gc_retain (; 35 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/increment + end + ) + (func $runtime/asrt/__gc_release (; 36 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if + local.get $0 + i32.const 16 + i32.sub + call $runtime/asrt/decrement + end + ) + (func $runtime/asrt/collectCycles (; 37 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -749,8 +2046,9 @@ i32.const 0 end if + global.get $runtime/asrt/ROOT local.get $4 - call $runtime/asrt/free + call $runtime/asrt/freeBlock else local.get $4 local.get $5 @@ -830,6 +2128,6 @@ local.get $0 global.set $runtime/asrt/CUR ) - (func $null (; 16 ;) (type $FUNCSIG$v) + (func $null (; 38 ;) (type $FUNCSIG$v) ) )