diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9d2f7db0a..dd9939c0aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,9 +81,9 @@ jobs: run: npm run build - name: Test experimental features env: - ASC_FEATURES: threads,reference-types,gc,exception-handling + ASC_FEATURES: threads,reference-types,gc,exception-handling,tail-calls run: | - npm run test:compiler features/threads features/reference-types features/gc features/exception-handling bindings/esm bindings/raw + npm run test:compiler features/threads features/reference-types features/gc features/exception-handling features/tail-calls bindings/esm bindings/raw runtimes: name: "Runtimes" runs-on: ubuntu-latest diff --git a/src/compiler.ts b/src/compiler.ts index f99bd5a049..0572f78ef1 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -218,6 +218,10 @@ import { ShadowStackPass } from "./passes/shadowstack"; +import { + TailCallPass +} from "./passes/tailcall"; + import { liftRequiresExportRuntime, lowerRequiresExportRuntime @@ -770,6 +774,10 @@ export class Compiler extends DiagnosticEmitter { new RtraceMemory(this).walkModule(); } + if (options.hasFeature(Feature.TailCalls)) { + new TailCallPass(module).walkFunctions(); + } + return module; } diff --git a/src/passes/tailcall.ts b/src/passes/tailcall.ts new file mode 100644 index 0000000000..6b44d05d02 --- /dev/null +++ b/src/passes/tailcall.ts @@ -0,0 +1,129 @@ +/** + * @fileoverview Detects tail calls after conversions and runtime instrumentation. + * @license Apache-2.0 + */ + +import { Pass } from "./pass"; +import { ExpressionId, ExpressionRef, FunctionRef, Index, TypeRef } from "../module"; +import { + _BinaryenBlockGetChildAt, + _BinaryenBlockGetName, + _BinaryenBlockGetNumChildren, + _BinaryenBlockSetChildAt, + _BinaryenCallSetReturn, + _BinaryenCallIndirectSetReturn, + _BinaryenExpressionFinalize, + _BinaryenExpressionGetId, + _BinaryenExpressionGetType, + _BinaryenFunctionGetBody, + _BinaryenFunctionGetResults, + _BinaryenFunctionSetBody, + _BinaryenIfGetIfTrue, + _BinaryenIfGetIfFalse, + _BinaryenIfSetIfTrue, + _BinaryenIfSetIfFalse, + _BinaryenLocalGetGetIndex, + _BinaryenLocalSetGetIndex, + _BinaryenLocalSetGetValue, + _BinaryenLocalSetIsTee, + _BinaryenReturnGetValue +} from "../glue/binaryen"; + +/** Converts calls only when their results reach the caller without further work. */ +export class TailCallPass extends Pass { + private results: TypeRef = TypeRef.None; + + /** @override */ + walkFunction(func: FunctionRef): void { + this.results = _BinaryenFunctionGetResults(func); + super.walkFunction(func); + let body = _BinaryenFunctionGetBody(func); + if (body) _BinaryenFunctionSetBody(func, this.makeTail(body)); + } + + /** @override */ + visit(expr: ExpressionRef): void { + // Returning through a handler must still allow that handler to catch exceptions + // thrown by the callee. Do not move calls out of exception regions. + if (_BinaryenExpressionGetId(expr) == ExpressionId.Try) return; + super.visit(expr); + _BinaryenExpressionFinalize(expr); + } + + /** @override */ + visitReturn(expr: ExpressionRef): void { + let value = _BinaryenReturnGetValue(expr); + if (value) { + value = this.makeTail(value); + if (_BinaryenExpressionGetType(value) == TypeRef.Unreachable) { + this.replaceCurrent(value); + } + } + } + + /** @override */ + visitBlock(expr: ExpressionRef): void { + let count = _BinaryenBlockGetNumChildren(expr); + for (let i: Index = 1; i < count; ++i) { + let ret = _BinaryenBlockGetChildAt(expr, i); + if (_BinaryenExpressionGetId(ret) != ExpressionId.Return) continue; + let value = _BinaryenReturnGetValue(ret); + let previous = _BinaryenBlockGetChildAt(expr, i - 1); + if (value) { + // The adjacent store cannot be observed after returning. Never move a + // call across intervening work, including shadow-stack restoration. + if ( + _BinaryenExpressionGetId(value) != ExpressionId.LocalGet || + _BinaryenExpressionGetId(previous) != ExpressionId.LocalSet || + _BinaryenLocalSetIsTee(previous) || + _BinaryenLocalGetGetIndex(value) != _BinaryenLocalSetGetIndex(previous) + ) continue; + value = this.makeTail(_BinaryenLocalSetGetValue(previous)); + } else { + if (this.results != TypeRef.None) continue; + value = this.makeTail(previous); + } + if (_BinaryenExpressionGetType(value) == TypeRef.Unreachable) { + _BinaryenBlockSetChildAt(expr, i - 1, this.module.nop()); + _BinaryenBlockSetChildAt(expr, i, value); + } + } + } + + /** Follows value-preserving wrappers, leaving conversions and cleanup intact. */ + private makeTail(expr: ExpressionRef): ExpressionRef { + switch (_BinaryenExpressionGetId(expr)) { + case ExpressionId.Call: { + if (_BinaryenExpressionGetType(expr) == this.results) { + _BinaryenCallSetReturn(expr, true); + } + break; + } + case ExpressionId.CallIndirect: { + if (_BinaryenExpressionGetType(expr) == this.results) { + _BinaryenCallIndirectSetReturn(expr, true); + } + break; + } + case ExpressionId.Block: { + // Named blocks can also receive values from branches to their label. + if (_BinaryenBlockGetName(expr)) break; + let count = _BinaryenBlockGetNumChildren(expr); + if (count) { + let last = _BinaryenBlockGetChildAt(expr, count - 1); + _BinaryenBlockSetChildAt(expr, count - 1, this.makeTail(last)); + } + break; + } + case ExpressionId.If: { + let ifTrue = _BinaryenIfGetIfTrue(expr); + let ifFalse = _BinaryenIfGetIfFalse(expr); + _BinaryenIfSetIfTrue(expr, this.makeTail(ifTrue)); + if (ifFalse) _BinaryenIfSetIfFalse(expr, this.makeTail(ifFalse)); + break; + } + } + _BinaryenExpressionFinalize(expr); + return expr; + } +} diff --git a/tests/compiler/features/tail-calls.debug.wat b/tests/compiler/features/tail-calls.debug.wat new file mode 100644 index 0000000000..ec8dde3728 --- /dev/null +++ b/tests/compiler/features/tail-calls.debug.wat @@ -0,0 +1,3581 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (param i32 i32))) + (type $2 (func (param i32 i32) (result i32))) + (type $3 (func)) + (type $4 (func (result i32))) + (type $5 (func (param i32))) + (type $6 (func (param i32) (result i64))) + (type $7 (func (param i32 i32 i32))) + (type $8 (func (param i32 i32) (result i64))) + (type $9 (func (param i32 i32 i32 i32))) + (type $10 (func (param i32 i32 i64) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (import "env" "tailHost" (func $features/tail-calls/host (param i32) (result i32))) + (global $features/tail-calls/effects (mut i32) (i32.const 0)) + (global $features/tail-calls/indirectFunction (mut i32) (i32.const 32)) + (global $features/tail-calls/order (mut i32) (i32.const 0)) + (global $features/tail-calls/arrow i32 (i32.const 64)) + (global $features/tail-calls/indirectIndex (mut i32) (i32.const 0)) + (global $~argumentsLength (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) + (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) + (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) + (global $~lib/rt/__rtti_base i32 (i32.const 800)) + (global $~lib/memory/__data_end i32 (i32.const 848)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33616)) + (global $~lib/memory/__heap_base i32 (i32.const 33616)) + (memory $0 1) + (data $0 (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (data $1 (i32.const 44) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\02\00\00\00\00\00\00\00\00\00\00\00") + (data $2 (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") + (data $3 (i32.const 140) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $4 (i32.const 208) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $5 (i32.const 240) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $6 (i32.const 268) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00") + (data $7 (i32.const 332) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") + (data $8 (i32.const 384) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $9 (i32.const 412) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $10 (i32.const 476) "\1c\00\00\00\00\00\00\00\00\00\00\00\06\00\00\00\08\00\00\00\03\00\00\00\00\00\00\00\00\00\00\00") + (data $11 (i32.const 508) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\04\00\00\00\00\00\00\00\00\00\00\00") + (data $12 (i32.const 540) "\1c\00\00\00\00\00\00\00\00\00\00\00\07\00\00\00\08\00\00\00\05\00\00\00\00\00\00\00\00\00\00\00") + (data $13 (i32.const 572) "|\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00^\00\00\00U\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00\'\00n\00u\00l\00l\00\'\00 \00(\00n\00o\00t\00 \00a\00s\00s\00i\00g\00n\00e\00d\00 \00o\00r\00 \00f\00a\00i\00l\00e\00d\00 \00c\00a\00s\00t\00)\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $14 (i32.const 700) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00,\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00t\00a\00i\00l\00-\00c\00a\00l\00l\00s\00.\00t\00s\00") + (data $15 (i32.const 764) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\06\00\00\00\00\00\00\00\00\00\00\00") + (data $16 (i32.const 800) "\0b\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00 \00\00\00 \00\00\00 \00\00\00") + (table $0 7 7 funcref) + (elem $0 (i32.const 1) $features/tail-calls/inc $start:features/tail-calls~anonymous|0 $features/tail-calls/maybeBox $features/tail-calls/indirectStep $features/tail-calls/optional@varargs $features/tail-calls/rawStep) + (export "direct" (func $features/tail-calls/direct)) + (export "directVoid" (func $features/tail-calls/directVoid)) + (export "indirect" (func $features/tail-calls/indirect)) + (export "converted" (func $features/tail-calls/converted)) + (export "convertedIndirect" (func $features/tail-calls/convertedIndirect)) + (export "nullable" (func $features/tail-calls/nullable)) + (export "nullableIndirect" (func $features/tail-calls/nullableIndirect)) + (export "recursive" (func $features/tail-calls/recursive)) + (export "mutualA" (func $features/tail-calls/mutualA)) + (export "ternary" (func $features/tail-calls/ternary)) + (export "temporary" (func $features/tail-calls/temporary)) + (export "comma" (func $features/tail-calls/comma)) + (export "switchCall" (func $features/tail-calls/switchCall)) + (export "bothCalls" (func $features/tail-calls/bothCalls)) + (export "nestedArguments" (func $features/tail-calls/nestedArguments)) + (export "branches" (func $features/tail-calls/branches)) + (export "deepIndirect" (func $features/tail-calls/deepIndirect)) + (export "callTemporaryIndirect" (func $features/tail-calls/callTemporaryIndirect)) + (export "indirectOrder" (func $features/tail-calls/indirectOrder)) + (export "getOrder" (func $features/tail-calls/getOrder)) + (export "optionalDirect" (func $features/tail-calls/optionalDirect)) + (export "optionalIndirect" (func $features/tail-calls/optionalIndirect)) + (export "inlineCaller" (func $features/tail-calls/inlineCaller)) + (export "narrowed" (func $features/tail-calls/narrowed)) + (export "wrapped" (func $features/tail-calls/wrapped)) + (export "discarded" (func $features/tail-calls/discarded)) + (export "afterCall" (func $features/tail-calls/afterCall)) + (export "nonTail" (func $features/tail-calls/nonTail)) + (export "nonNull" (func $features/tail-calls/nonNull)) + (export "voidCall" (func $features/tail-calls/voidCall)) + (export "voidImplicit" (func $features/tail-calls/voidImplicit)) + (export "managed" (func $features/tail-calls/managed)) + (export "stackPointer" (func $features/tail-calls/stackPointer)) + (export "callArrow" (func $features/tail-calls/callArrow)) + (export "virtual" (func $features/tail-calls/virtual)) + (export "deepRaw" (func $features/tail-calls/deepRaw)) + (export "imported" (func $features/tail-calls/imported)) + (export "memory" (memory $0)) + (export "recursiveIndirect" (func $export:features/tail-calls/recursiveIndirect)) + (export "temporaryIndirect" (func $export:features/tail-calls/temporaryIndirect)) + (start $~start) + (func $features/tail-calls/inc (param $x i32) (result i32) + local.get $x + i32.const 1 + i32.add + return + ) + (func $start:features/tail-calls~anonymous|0 (param $x i32) (result i32) + local.get $x + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/direct (param $x i32) (result i32) + local.get $x + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/noop + ) + (func $features/tail-calls/directVoid + return_call $features/tail-calls/noop + ) + (func $features/tail-calls/invoke (param $fn i32) (param $x i32) (result i32) + local.get $x + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/converted (param $x i32) (result i64) + local.get $x + call $features/tail-calls/inc + i64.extend_i32_s + return + ) + (func $features/tail-calls/widenInvoke (param $fn i32) (param $x i32) (result i64) + local.get $x + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + call_indirect (type $0) + i64.extend_i32_s + return + ) + (func $~lib/rt/itcms/Object#set:nextWithColor (param $this i32) (param $nextWithColor i32) + local.get $this + local.get $nextWithColor + i32.store offset=4 + ) + (func $~lib/rt/itcms/Object#set:prev (param $this i32) (param $prev i32) + local.get $this + local.get $prev + i32.store offset=8 + ) + (func $~lib/rt/itcms/initLazy (param $space i32) (result i32) + local.get $space + local.get $space + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $space + local.get $space + call $~lib/rt/itcms/Object#set:prev + local.get $space + return + ) + (func $~lib/rt/itcms/Object#get:nextWithColor (param $this i32) (result i32) + local.get $this + i32.load offset=4 + ) + (func $~lib/rt/itcms/Object#get:next (param $this i32) (result i32) + local.get $this + call $~lib/rt/itcms/Object#get:nextWithColor + i32.const 3 + i32.const -1 + i32.xor + i32.and + return + ) + (func $~lib/rt/itcms/Object#get:color (param $this i32) (result i32) + local.get $this + call $~lib/rt/itcms/Object#get:nextWithColor + i32.const 3 + i32.and + return + ) + (func $~lib/rt/itcms/visitRoots (param $cookie i32) + (local $pn i32) + (local $iter i32) + local.get $cookie + call $~lib/rt/__visit_globals + global.get $~lib/rt/itcms/pinSpace + local.set $pn + local.get $pn + call $~lib/rt/itcms/Object#get:next + local.set $iter + loop $while-continue|0 + local.get $iter + local.get $pn + i32.ne + if + i32.const 1 + drop + local.get $iter + call $~lib/rt/itcms/Object#get:color + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 160 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $iter + i32.const 20 + i32.add + local.get $cookie + call $~lib/rt/__visit_members + local.get $iter + call $~lib/rt/itcms/Object#get:next + local.set $iter + br $while-continue|0 + end + end + ) + (func $~lib/rt/itcms/Object#set:color (param $this i32) (param $color i32) + local.get $this + local.get $this + call $~lib/rt/itcms/Object#get:nextWithColor + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $color + i32.or + return_call $~lib/rt/itcms/Object#set:nextWithColor + ) + (func $~lib/rt/itcms/Object#get:prev (param $this i32) (result i32) + local.get $this + i32.load offset=8 + ) + (func $~lib/rt/itcms/Object#set:next (param $this i32) (param $obj i32) + local.get $this + local.get $obj + local.get $this + call $~lib/rt/itcms/Object#get:nextWithColor + i32.const 3 + i32.and + i32.or + return_call $~lib/rt/itcms/Object#set:nextWithColor + ) + (func $~lib/rt/itcms/Object#unlink (param $this i32) + (local $next i32) + (local $prev i32) + local.get $this + call $~lib/rt/itcms/Object#get:next + local.set $next + local.get $next + i32.const 0 + i32.eq + if + i32.const 1 + drop + local.get $this + call $~lib/rt/itcms/Object#get:prev + i32.const 0 + i32.eq + if (result i32) + local.get $this + global.get $~lib/memory/__heap_base + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 128 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + return + end + local.get $this + call $~lib/rt/itcms/Object#get:prev + local.set $prev + i32.const 1 + drop + local.get $prev + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 132 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $next + local.get $prev + call $~lib/rt/itcms/Object#set:prev + local.get $prev + local.get $next + return_call $~lib/rt/itcms/Object#set:next + ) + (func $~lib/rt/itcms/Object#get:rtId (param $this i32) (result i32) + local.get $this + i32.load offset=12 + ) + (func $~lib/shared/typeinfo/Typeinfo#get:flags (param $this i32) (result i32) + local.get $this + i32.load + ) + (func $~lib/rt/__typeinfo (param $id i32) (result i32) + (local $ptr i32) + global.get $~lib/rt/__rtti_base + local.set $ptr + local.get $id + local.get $ptr + i32.load + i32.gt_u + if + i32.const 288 + i32.const 352 + i32.const 21 + i32.const 28 + call $~lib/builtins/abort + unreachable + end + local.get $ptr + i32.const 4 + i32.add + local.get $id + i32.const 4 + i32.mul + i32.add + return_call $~lib/shared/typeinfo/Typeinfo#get:flags + ) + (func $~lib/rt/itcms/Object#get:isPointerfree (param $this i32) (result i32) + (local $rtId i32) + local.get $this + call $~lib/rt/itcms/Object#get:rtId + local.set $rtId + local.get $rtId + i32.const 2 + i32.le_u + if (result i32) + i32.const 1 + else + local.get $rtId + call $~lib/rt/__typeinfo + i32.const 32 + i32.and + i32.const 0 + i32.ne + end + return + ) + (func $~lib/rt/itcms/Object#linkTo (param $this i32) (param $list i32) (param $withColor i32) + (local $prev i32) + local.get $list + call $~lib/rt/itcms/Object#get:prev + local.set $prev + local.get $this + local.get $list + local.get $withColor + i32.or + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $this + local.get $prev + call $~lib/rt/itcms/Object#set:prev + local.get $prev + local.get $this + call $~lib/rt/itcms/Object#set:next + local.get $list + local.get $this + return_call $~lib/rt/itcms/Object#set:prev + ) + (func $~lib/rt/itcms/Object#makeGray (param $this i32) + (local $1 i32) + local.get $this + global.get $~lib/rt/itcms/iter + i32.eq + if + local.get $this + call $~lib/rt/itcms/Object#get:prev + local.tee $1 + i32.eqz + if (result i32) + i32.const 0 + i32.const 160 + i32.const 148 + i32.const 30 + call $~lib/builtins/abort + unreachable + else + local.get $1 + end + global.set $~lib/rt/itcms/iter + end + local.get $this + call $~lib/rt/itcms/Object#unlink + local.get $this + global.get $~lib/rt/itcms/toSpace + local.get $this + call $~lib/rt/itcms/Object#get:isPointerfree + if (result i32) + global.get $~lib/rt/itcms/white + i32.eqz + else + i32.const 2 + end + return_call $~lib/rt/itcms/Object#linkTo + ) + (func $~lib/rt/itcms/__visit (param $ptr i32) (param $cookie i32) + (local $obj i32) + local.get $ptr + i32.eqz + if + return + end + local.get $ptr + i32.const 20 + i32.sub + local.set $obj + i32.const 0 + drop + local.get $obj + call $~lib/rt/itcms/Object#get:color + global.get $~lib/rt/itcms/white + i32.eq + if + local.get $obj + call $~lib/rt/itcms/Object#makeGray + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.add + global.set $~lib/rt/itcms/visitCount + end + ) + (func $~lib/rt/itcms/visitStack (param $cookie i32) + (local $ptr i32) + global.get $~lib/memory/__stack_pointer + local.set $ptr + loop $while-continue|0 + local.get $ptr + global.get $~lib/memory/__heap_base + i32.lt_u + if + local.get $ptr + i32.load + local.get $cookie + call $~lib/rt/itcms/__visit + local.get $ptr + i32.const 4 + i32.add + local.set $ptr + br $while-continue|0 + end + end + ) + (func $~lib/rt/common/BLOCK#get:mmInfo (param $this i32) (result i32) + local.get $this + i32.load + ) + (func $~lib/rt/itcms/Object#get:size (param $this i32) (result i32) + i32.const 4 + local.get $this + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + return + ) + (func $~lib/rt/tlsf/Root#set:flMap (param $this i32) (param $flMap i32) + local.get $this + local.get $flMap + i32.store + ) + (func $~lib/rt/common/BLOCK#set:mmInfo (param $this i32) (param $mmInfo i32) + local.get $this + local.get $mmInfo + i32.store + ) + (func $~lib/rt/tlsf/Block#set:prev (param $this i32) (param $prev i32) + local.get $this + local.get $prev + i32.store offset=4 + ) + (func $~lib/rt/tlsf/Block#set:next (param $this i32) (param $next i32) + local.get $this + local.get $next + i32.store offset=8 + ) + (func $~lib/rt/tlsf/Block#get:prev (param $this i32) (result i32) + local.get $this + i32.load offset=4 + ) + (func $~lib/rt/tlsf/Block#get:next (param $this i32) (result i32) + local.get $this + i32.load offset=8 + ) + (func $~lib/rt/tlsf/Root#get:flMap (param $this i32) (result i32) + local.get $this + i32.load + ) + (func $~lib/rt/tlsf/removeBlock (param $root i32) (param $block i32) + (local $blockInfo i32) + (local $size i32) + (local $fl i32) + (local $sl i32) + (local $6 i32) + (local $7 i32) + (local $boundedSize i32) + (local $prev i32) + (local $next i32) + (local $root|11 i32) + (local $fl|12 i32) + (local $sl|13 i32) + (local $root|14 i32) + (local $fl|15 i32) + (local $sl|16 i32) + (local $head i32) + (local $root|18 i32) + (local $fl|19 i32) + (local $slMap i32) + (local $root|21 i32) + (local $fl|22 i32) + (local $slMap|23 i32) + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $blockInfo + i32.const 1 + drop + local.get $blockInfo + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 268 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $blockInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $size + i32.const 1 + drop + local.get $size + i32.const 12 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 270 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $size + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $fl + local.get $size + i32.const 4 + i32.shr_u + local.set $sl + else + local.get $size + local.tee $6 + i32.const 1073741820 + local.tee $7 + local.get $6 + local.get $7 + i32.lt_u + select + local.set $boundedSize + i32.const 31 + local.get $boundedSize + i32.clz + i32.sub + local.set $fl + local.get $boundedSize + local.get $fl + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $sl + local.get $fl + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $fl + end + i32.const 1 + drop + local.get $fl + i32.const 23 + i32.lt_u + if (result i32) + local.get $sl + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 284 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $block + call $~lib/rt/tlsf/Block#get:prev + local.set $prev + local.get $block + call $~lib/rt/tlsf/Block#get:next + local.set $next + local.get $prev + if + local.get $prev + local.get $next + call $~lib/rt/tlsf/Block#set:next + end + local.get $next + if + local.get $next + local.get $prev + call $~lib/rt/tlsf/Block#set:prev + end + local.get $block + block $~lib/rt/tlsf/GETHEAD|inlined.0 (result i32) + local.get $root + local.set $root|11 + local.get $fl + local.set $fl|12 + local.get $sl + local.set $sl|13 + local.get $root|11 + local.get $fl|12 + i32.const 4 + i32.shl + local.get $sl|13 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + br $~lib/rt/tlsf/GETHEAD|inlined.0 + end + i32.eq + if + local.get $root + local.set $root|14 + local.get $fl + local.set $fl|15 + local.get $sl + local.set $sl|16 + local.get $next + local.set $head + local.get $root|14 + local.get $fl|15 + i32.const 4 + i32.shl + local.get $sl|16 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $head + i32.store offset=96 + local.get $next + i32.eqz + if + block $~lib/rt/tlsf/GETSL|inlined.0 (result i32) + local.get $root + local.set $root|18 + local.get $fl + local.set $fl|19 + local.get $root|18 + local.get $fl|19 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + br $~lib/rt/tlsf/GETSL|inlined.0 + end + local.set $slMap + local.get $root + local.set $root|21 + local.get $fl + local.set $fl|22 + local.get $slMap + i32.const 1 + local.get $sl + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $slMap + local.set $slMap|23 + local.get $root|21 + local.get $fl|22 + i32.const 2 + i32.shl + i32.add + local.get $slMap|23 + i32.store offset=4 + local.get $slMap + i32.eqz + if + local.get $root + local.get $root + call $~lib/rt/tlsf/Root#get:flMap + i32.const 1 + local.get $fl + i32.shl + i32.const -1 + i32.xor + i32.and + return_call $~lib/rt/tlsf/Root#set:flMap + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $root i32) (param $block i32) + (local $blockInfo i32) + (local $block|3 i32) + (local $right i32) + (local $rightInfo i32) + (local $block|6 i32) + (local $block|7 i32) + (local $left i32) + (local $leftInfo i32) + (local $size i32) + (local $fl i32) + (local $sl i32) + (local $13 i32) + (local $14 i32) + (local $boundedSize i32) + (local $root|16 i32) + (local $fl|17 i32) + (local $sl|18 i32) + (local $head i32) + (local $root|20 i32) + (local $fl|21 i32) + (local $sl|22 i32) + (local $head|23 i32) + (local $root|24 i32) + (local $fl|25 i32) + (local $root|26 i32) + (local $fl|27 i32) + (local $slMap i32) + i32.const 1 + drop + local.get $block + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 201 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $blockInfo + i32.const 1 + drop + local.get $blockInfo + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 203 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + block $~lib/rt/tlsf/GETRIGHT|inlined.0 (result i32) + local.get $block + local.set $block|3 + local.get $block|3 + i32.const 4 + i32.add + local.get $block|3 + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + br $~lib/rt/tlsf/GETRIGHT|inlined.0 + end + local.set $right + local.get $right + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $rightInfo + local.get $rightInfo + i32.const 1 + i32.and + if + local.get $root + local.get $right + call $~lib/rt/tlsf/removeBlock + local.get $block + local.get $blockInfo + i32.const 4 + i32.add + local.get $rightInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.tee $blockInfo + call $~lib/rt/common/BLOCK#set:mmInfo + block $~lib/rt/tlsf/GETRIGHT|inlined.1 (result i32) + local.get $block + local.set $block|6 + local.get $block|6 + i32.const 4 + i32.add + local.get $block|6 + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + br $~lib/rt/tlsf/GETRIGHT|inlined.1 + end + local.set $right + local.get $right + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $rightInfo + end + local.get $blockInfo + i32.const 2 + i32.and + if + block $~lib/rt/tlsf/GETFREELEFT|inlined.0 (result i32) + local.get $block + local.set $block|7 + local.get $block|7 + i32.const 4 + i32.sub + i32.load + br $~lib/rt/tlsf/GETFREELEFT|inlined.0 + end + local.set $left + local.get $left + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $leftInfo + i32.const 1 + drop + local.get $leftInfo + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 221 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $root + local.get $left + call $~lib/rt/tlsf/removeBlock + local.get $left + local.set $block + local.get $block + local.get $leftInfo + i32.const 4 + i32.add + local.get $blockInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.tee $blockInfo + call $~lib/rt/common/BLOCK#set:mmInfo + end + local.get $right + local.get $rightInfo + i32.const 2 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $blockInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $size + i32.const 1 + drop + local.get $size + i32.const 12 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 233 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + drop + local.get $block + i32.const 4 + i32.add + local.get $size + i32.add + local.get $right + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 234 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $right + i32.const 4 + i32.sub + local.get $block + i32.store + local.get $size + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $fl + local.get $size + i32.const 4 + i32.shr_u + local.set $sl + else + local.get $size + local.tee $13 + i32.const 1073741820 + local.tee $14 + local.get $13 + local.get $14 + i32.lt_u + select + local.set $boundedSize + i32.const 31 + local.get $boundedSize + i32.clz + i32.sub + local.set $fl + local.get $boundedSize + local.get $fl + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $sl + local.get $fl + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $fl + end + i32.const 1 + drop + local.get $fl + i32.const 23 + i32.lt_u + if (result i32) + local.get $sl + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 251 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + block $~lib/rt/tlsf/GETHEAD|inlined.1 (result i32) + local.get $root + local.set $root|16 + local.get $fl + local.set $fl|17 + local.get $sl + local.set $sl|18 + local.get $root|16 + local.get $fl|17 + i32.const 4 + i32.shl + local.get $sl|18 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + br $~lib/rt/tlsf/GETHEAD|inlined.1 + end + local.set $head + local.get $block + i32.const 0 + call $~lib/rt/tlsf/Block#set:prev + local.get $block + local.get $head + call $~lib/rt/tlsf/Block#set:next + local.get $head + if + local.get $head + local.get $block + call $~lib/rt/tlsf/Block#set:prev + end + local.get $root + local.set $root|20 + local.get $fl + local.set $fl|21 + local.get $sl + local.set $sl|22 + local.get $block + local.set $head|23 + local.get $root|20 + local.get $fl|21 + i32.const 4 + i32.shl + local.get $sl|22 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $head|23 + i32.store offset=96 + local.get $root + local.get $root + call $~lib/rt/tlsf/Root#get:flMap + i32.const 1 + local.get $fl + i32.shl + i32.or + call $~lib/rt/tlsf/Root#set:flMap + local.get $root + local.set $root|26 + local.get $fl + local.set $fl|27 + block $~lib/rt/tlsf/GETSL|inlined.1 (result i32) + local.get $root + local.set $root|24 + local.get $fl + local.set $fl|25 + local.get $root|24 + local.get $fl|25 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + br $~lib/rt/tlsf/GETSL|inlined.1 + end + i32.const 1 + local.get $sl + i32.shl + i32.or + local.set $slMap + local.get $root|26 + local.get $fl|27 + i32.const 2 + i32.shl + i32.add + local.get $slMap + i32.store offset=4 + ) + (func $~lib/rt/tlsf/addMemory (param $root i32) (param $start i32) (param $endU64 i64) (result i32) + (local $end i32) + (local $root|4 i32) + (local $tail i32) + (local $tailInfo i32) + (local $size i32) + (local $leftSize i32) + (local $left i32) + (local $root|10 i32) + (local $tail|11 i32) + local.get $endU64 + i32.wrap_i64 + local.set $end + i32.const 1 + drop + local.get $start + i64.extend_i32_u + local.get $endU64 + i64.le_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 382 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $start + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + local.set $start + local.get $end + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $end + block $~lib/rt/tlsf/GETTAIL|inlined.0 (result i32) + local.get $root + local.set $root|4 + local.get $root|4 + i32.load offset=1568 + br $~lib/rt/tlsf/GETTAIL|inlined.0 + end + local.set $tail + i32.const 0 + local.set $tailInfo + local.get $tail + if + i32.const 1 + drop + local.get $start + local.get $tail + i32.const 4 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 389 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $start + i32.const 16 + i32.sub + local.get $tail + i32.eq + if + local.get $start + i32.const 16 + i32.sub + local.set $start + local.get $tail + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $tailInfo + else + end + else + i32.const 1 + drop + local.get $start + local.get $root + i32.const 1572 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 402 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $end + local.get $start + i32.sub + local.set $size + local.get $size + i32.const 4 + i32.const 12 + i32.add + i32.const 4 + i32.add + i32.lt_u + if + i32.const 0 + return + end + local.get $size + i32.const 2 + i32.const 4 + i32.mul + i32.sub + local.set $leftSize + local.get $start + local.set $left + local.get $left + local.get $leftSize + i32.const 1 + i32.or + local.get $tailInfo + i32.const 2 + i32.and + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $left + i32.const 0 + call $~lib/rt/tlsf/Block#set:prev + local.get $left + i32.const 0 + call $~lib/rt/tlsf/Block#set:next + local.get $start + i32.const 4 + i32.add + local.get $leftSize + i32.add + local.set $tail + local.get $tail + i32.const 0 + i32.const 2 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $root + local.set $root|10 + local.get $tail + local.set $tail|11 + local.get $root|10 + local.get $tail|11 + i32.store offset=1568 + local.get $root + local.get $left + call $~lib/rt/tlsf/insertBlock + i32.const 1 + return + ) + (func $~lib/rt/tlsf/initialize + (local $rootOffset i32) + (local $pagesBefore i32) + (local $pagesNeeded i32) + (local $root i32) + (local $root|4 i32) + (local $tail i32) + (local $fl i32) + (local $root|7 i32) + (local $fl|8 i32) + (local $slMap i32) + (local $sl i32) + (local $root|11 i32) + (local $fl|12 i32) + (local $sl|13 i32) + (local $head i32) + (local $memStart i32) + i32.const 0 + drop + global.get $~lib/memory/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $rootOffset + memory.size + local.set $pagesBefore + local.get $rootOffset + i32.const 1572 + 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 $pagesNeeded + local.get $pagesNeeded + local.get $pagesBefore + i32.gt_s + if (result i32) + local.get $pagesNeeded + local.get $pagesBefore + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + local.get $rootOffset + local.set $root + local.get $root + i32.const 0 + call $~lib/rt/tlsf/Root#set:flMap + local.get $root + local.set $root|4 + i32.const 0 + local.set $tail + local.get $root|4 + local.get $tail + i32.store offset=1568 + i32.const 0 + local.set $fl + loop $for-loop|0 + local.get $fl + i32.const 23 + i32.lt_u + if + local.get $root + local.set $root|7 + local.get $fl + local.set $fl|8 + i32.const 0 + local.set $slMap + local.get $root|7 + local.get $fl|8 + i32.const 2 + i32.shl + i32.add + local.get $slMap + i32.store offset=4 + i32.const 0 + local.set $sl + loop $for-loop|1 + local.get $sl + i32.const 16 + i32.lt_u + if + local.get $root + local.set $root|11 + local.get $fl + local.set $fl|12 + local.get $sl + local.set $sl|13 + i32.const 0 + local.set $head + local.get $root|11 + local.get $fl|12 + i32.const 4 + i32.shl + local.get $sl|13 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $head + i32.store offset=96 + local.get $sl + i32.const 1 + i32.add + local.set $sl + br $for-loop|1 + end + end + local.get $fl + i32.const 1 + i32.add + local.set $fl + br $for-loop|0 + end + end + local.get $rootOffset + i32.const 1572 + i32.add + local.set $memStart + i32.const 0 + drop + local.get $root + local.get $memStart + memory.size + i64.extend_i32_s + i64.const 16 + i64.shl + call $~lib/rt/tlsf/addMemory + drop + local.get $root + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/tlsf/checkUsedBlock (param $ptr i32) (result i32) + (local $block i32) + local.get $ptr + i32.const 4 + i32.sub + local.set $block + local.get $ptr + i32.const 0 + i32.ne + if (result i32) + local.get $ptr + i32.const 15 + i32.and + i32.eqz + else + i32.const 0 + end + if (result i32) + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 1 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 562 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $block + return + ) + (func $~lib/rt/tlsf/freeBlock (param $root i32) (param $block i32) + i32.const 0 + drop + local.get $block + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 1 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $root + local.get $block + return_call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/__free (param $ptr i32) + local.get $ptr + global.get $~lib/memory/__heap_base + i32.lt_u + if + return + end + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $ptr + call $~lib/rt/tlsf/checkUsedBlock + return_call $~lib/rt/tlsf/freeBlock + ) + (func $~lib/rt/itcms/free (param $obj i32) + local.get $obj + global.get $~lib/memory/__heap_base + i32.lt_u + if + local.get $obj + i32.const 0 + call $~lib/rt/itcms/Object#set:nextWithColor + local.get $obj + i32.const 0 + return_call $~lib/rt/itcms/Object#set:prev + else + global.get $~lib/rt/itcms/total + local.get $obj + call $~lib/rt/itcms/Object#get:size + i32.sub + global.set $~lib/rt/itcms/total + i32.const 0 + drop + local.get $obj + i32.const 4 + i32.add + return_call $~lib/rt/tlsf/__free + end + unreachable + ) + (func $~lib/rt/itcms/step (result i32) + (local $obj i32) + (local $1 i32) + (local $black i32) + (local $from i32) + block $break|0 + block $case2|0 + block $case1|0 + block $case0|0 + global.get $~lib/rt/itcms/state + local.set $1 + local.get $1 + i32.const 0 + i32.eq + br_if $case0|0 + local.get $1 + i32.const 1 + i32.eq + br_if $case1|0 + local.get $1 + i32.const 2 + i32.eq + br_if $case2|0 + br $break|0 + end + i32.const 1 + global.set $~lib/rt/itcms/state + i32.const 0 + global.set $~lib/rt/itcms/visitCount + i32.const 0 + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + global.get $~lib/rt/itcms/white + i32.eqz + local.set $black + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $obj + loop $while-continue|1 + local.get $obj + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $obj + global.set $~lib/rt/itcms/iter + local.get $obj + call $~lib/rt/itcms/Object#get:color + local.get $black + i32.ne + if + local.get $obj + local.get $black + call $~lib/rt/itcms/Object#set:color + i32.const 0 + global.set $~lib/rt/itcms/visitCount + local.get $obj + i32.const 20 + i32.add + i32.const 0 + call $~lib/rt/__visit_members + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + local.get $obj + call $~lib/rt/itcms/Object#get:next + local.set $obj + br $while-continue|1 + end + end + i32.const 0 + global.set $~lib/rt/itcms/visitCount + i32.const 0 + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $obj + local.get $obj + global.get $~lib/rt/itcms/toSpace + i32.eq + if + i32.const 0 + call $~lib/rt/itcms/visitStack + global.get $~lib/rt/itcms/iter + call $~lib/rt/itcms/Object#get:next + local.set $obj + loop $while-continue|2 + local.get $obj + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $obj + call $~lib/rt/itcms/Object#get:color + local.get $black + i32.ne + if + local.get $obj + local.get $black + call $~lib/rt/itcms/Object#set:color + local.get $obj + i32.const 20 + i32.add + i32.const 0 + call $~lib/rt/__visit_members + end + local.get $obj + call $~lib/rt/itcms/Object#get:next + local.set $obj + br $while-continue|2 + end + end + global.get $~lib/rt/itcms/fromSpace + local.set $from + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/fromSpace + local.get $from + global.set $~lib/rt/itcms/toSpace + local.get $black + global.set $~lib/rt/itcms/white + local.get $from + call $~lib/rt/itcms/Object#get:next + global.set $~lib/rt/itcms/iter + i32.const 2 + global.set $~lib/rt/itcms/state + end + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.mul + return + end + global.get $~lib/rt/itcms/iter + local.set $obj + local.get $obj + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $obj + call $~lib/rt/itcms/Object#get:next + global.set $~lib/rt/itcms/iter + i32.const 1 + drop + local.get $obj + call $~lib/rt/itcms/Object#get:color + global.get $~lib/rt/itcms/white + i32.eqz + i32.eq + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 229 + i32.const 20 + call $~lib/builtins/abort + unreachable + end + local.get $obj + call $~lib/rt/itcms/free + i32.const 10 + return + end + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + call $~lib/rt/itcms/Object#set:nextWithColor + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + call $~lib/rt/itcms/Object#set:prev + i32.const 0 + global.set $~lib/rt/itcms/state + br $break|0 + end + i32.const 0 + return + ) + (func $~lib/rt/itcms/interrupt + (local $budget i32) + i32.const 0 + drop + i32.const 0 + drop + i32.const 1024 + i32.const 200 + i32.mul + i32.const 100 + i32.div_u + local.set $budget + loop $do-loop|0 + local.get $budget + call $~lib/rt/itcms/step + i32.sub + local.set $budget + global.get $~lib/rt/itcms/state + i32.const 0 + i32.eq + if + i32.const 0 + drop + i32.const 200 + i32.const 100 + i32.rem_u + i32.const 0 + i32.eq + drop + global.get $~lib/rt/itcms/total + i32.const 200 + i32.const 100 + i32.div_u + i32.mul + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + i32.const 0 + drop + return + end + local.get $budget + i32.const 0 + i32.gt_s + br_if $do-loop|0 + end + i32.const 0 + drop + global.get $~lib/rt/itcms/total + i32.const 1024 + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.sub + i32.const 1024 + i32.lt_u + i32.mul + i32.add + global.set $~lib/rt/itcms/threshold + i32.const 0 + drop + ) + (func $~lib/rt/tlsf/computeSize (param $size i32) (result i32) + local.get $size + i32.const 12 + i32.le_u + if (result i32) + i32.const 12 + else + local.get $size + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + end + return + ) + (func $~lib/rt/tlsf/prepareSize (param $size i32) (result i32) + local.get $size + i32.const 1073741820 + i32.gt_u + if + i32.const 96 + i32.const 432 + i32.const 461 + i32.const 29 + call $~lib/builtins/abort + unreachable + end + local.get $size + return_call $~lib/rt/tlsf/computeSize + ) + (func $~lib/rt/tlsf/roundSize (param $size i32) (result i32) + local.get $size + i32.const 536870910 + i32.lt_u + if (result i32) + local.get $size + i32.const 1 + i32.const 27 + local.get $size + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + else + local.get $size + end + return + ) + (func $~lib/rt/tlsf/searchBlock (param $root i32) (param $size i32) (result i32) + (local $fl i32) + (local $sl i32) + (local $requestSize i32) + (local $root|5 i32) + (local $fl|6 i32) + (local $slMap i32) + (local $head i32) + (local $flMap i32) + (local $root|10 i32) + (local $fl|11 i32) + (local $root|12 i32) + (local $fl|13 i32) + (local $sl|14 i32) + (local $root|15 i32) + (local $fl|16 i32) + (local $sl|17 i32) + local.get $size + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $fl + local.get $size + i32.const 4 + i32.shr_u + local.set $sl + else + local.get $size + call $~lib/rt/tlsf/roundSize + local.set $requestSize + i32.const 4 + i32.const 8 + i32.mul + i32.const 1 + i32.sub + local.get $requestSize + i32.clz + i32.sub + local.set $fl + local.get $requestSize + local.get $fl + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $sl + local.get $fl + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $fl + end + i32.const 1 + drop + local.get $fl + i32.const 23 + i32.lt_u + if (result i32) + local.get $sl + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 334 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + block $~lib/rt/tlsf/GETSL|inlined.2 (result i32) + local.get $root + local.set $root|5 + local.get $fl + local.set $fl|6 + local.get $root|5 + local.get $fl|6 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + br $~lib/rt/tlsf/GETSL|inlined.2 + end + i32.const 0 + i32.const -1 + i32.xor + local.get $sl + i32.shl + i32.and + local.set $slMap + i32.const 0 + local.set $head + local.get $slMap + i32.eqz + if + local.get $root + call $~lib/rt/tlsf/Root#get:flMap + i32.const 0 + i32.const -1 + i32.xor + local.get $fl + i32.const 1 + i32.add + i32.shl + i32.and + local.set $flMap + local.get $flMap + i32.eqz + if + i32.const 0 + local.set $head + else + local.get $flMap + i32.ctz + local.set $fl + block $~lib/rt/tlsf/GETSL|inlined.3 (result i32) + local.get $root + local.set $root|10 + local.get $fl + local.set $fl|11 + local.get $root|10 + local.get $fl|11 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + br $~lib/rt/tlsf/GETSL|inlined.3 + end + local.set $slMap + i32.const 1 + drop + local.get $slMap + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 347 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + block $~lib/rt/tlsf/GETHEAD|inlined.2 (result i32) + local.get $root + local.set $root|12 + local.get $fl + local.set $fl|13 + local.get $slMap + i32.ctz + local.set $sl|14 + local.get $root|12 + local.get $fl|13 + i32.const 4 + i32.shl + local.get $sl|14 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + br $~lib/rt/tlsf/GETHEAD|inlined.2 + end + local.set $head + end + else + block $~lib/rt/tlsf/GETHEAD|inlined.3 (result i32) + local.get $root + local.set $root|15 + local.get $fl + local.set $fl|16 + local.get $slMap + i32.ctz + local.set $sl|17 + local.get $root|15 + local.get $fl|16 + i32.const 4 + i32.shl + local.get $sl|17 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + br $~lib/rt/tlsf/GETHEAD|inlined.3 + end + local.set $head + end + local.get $head + return + ) + (func $~lib/rt/tlsf/growMemory (param $root i32) (param $size i32) + (local $pagesBefore i32) + (local $root|3 i32) + (local $pagesNeeded i32) + (local $5 i32) + (local $6 i32) + (local $pagesWanted i32) + (local $pagesAfter i32) + i32.const 0 + drop + local.get $size + i32.const 256 + i32.ge_u + if + local.get $size + call $~lib/rt/tlsf/roundSize + local.set $size + end + memory.size + local.set $pagesBefore + local.get $size + i32.const 4 + local.get $pagesBefore + i32.const 16 + i32.shl + i32.const 4 + i32.sub + block $~lib/rt/tlsf/GETTAIL|inlined.1 (result i32) + local.get $root + local.set $root|3 + local.get $root|3 + i32.load offset=1568 + br $~lib/rt/tlsf/GETTAIL|inlined.1 + end + i32.ne + i32.shl + i32.add + local.set $size + local.get $size + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $pagesNeeded + local.get $pagesBefore + local.tee $5 + local.get $pagesNeeded + local.tee $6 + local.get $5 + local.get $6 + i32.gt_s + select + local.set $pagesWanted + local.get $pagesWanted + memory.grow + i32.const 0 + i32.lt_s + if + local.get $pagesNeeded + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + memory.size + local.set $pagesAfter + local.get $root + local.get $pagesBefore + i32.const 16 + i32.shl + local.get $pagesAfter + i64.extend_i32_s + i64.const 16 + i64.shl + call $~lib/rt/tlsf/addMemory + drop + ) + (func $~lib/rt/tlsf/prepareBlock (param $root i32) (param $block i32) (param $size i32) + (local $blockInfo i32) + (local $remaining i32) + (local $spare i32) + (local $block|6 i32) + (local $block|7 i32) + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + local.set $blockInfo + i32.const 1 + drop + local.get $size + i32.const 4 + i32.add + i32.const 15 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 361 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $blockInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $size + i32.sub + local.set $remaining + local.get $remaining + i32.const 4 + i32.const 12 + i32.add + i32.ge_u + if + local.get $block + local.get $size + local.get $blockInfo + i32.const 2 + i32.and + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $block + i32.const 4 + i32.add + local.get $size + i32.add + local.set $spare + local.get $spare + local.get $remaining + i32.const 4 + i32.sub + i32.const 1 + i32.or + call $~lib/rt/common/BLOCK#set:mmInfo + local.get $root + local.get $spare + return_call $~lib/rt/tlsf/insertBlock + else + local.get $block + local.get $blockInfo + i32.const 1 + i32.const -1 + i32.xor + i32.and + call $~lib/rt/common/BLOCK#set:mmInfo + block $~lib/rt/tlsf/GETRIGHT|inlined.3 (result i32) + local.get $block + local.set $block|7 + local.get $block|7 + i32.const 4 + i32.add + local.get $block|7 + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + br $~lib/rt/tlsf/GETRIGHT|inlined.3 + end + block $~lib/rt/tlsf/GETRIGHT|inlined.2 (result i32) + local.get $block + local.set $block|6 + local.get $block|6 + i32.const 4 + i32.add + local.get $block|6 + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + br $~lib/rt/tlsf/GETRIGHT|inlined.2 + end + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 2 + i32.const -1 + i32.xor + i32.and + return_call $~lib/rt/common/BLOCK#set:mmInfo + end + unreachable + ) + (func $~lib/rt/tlsf/allocateBlock (param $root i32) (param $size i32) (result i32) + (local $payloadSize i32) + (local $block i32) + local.get $size + call $~lib/rt/tlsf/prepareSize + local.set $payloadSize + local.get $root + local.get $payloadSize + call $~lib/rt/tlsf/searchBlock + local.set $block + local.get $block + i32.eqz + if + local.get $root + local.get $payloadSize + call $~lib/rt/tlsf/growMemory + local.get $root + local.get $payloadSize + call $~lib/rt/tlsf/searchBlock + local.set $block + i32.const 1 + drop + local.get $block + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 499 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + i32.const 1 + drop + local.get $block + call $~lib/rt/common/BLOCK#get:mmInfo + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $payloadSize + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 501 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $root + local.get $block + call $~lib/rt/tlsf/removeBlock + local.get $root + local.get $block + local.get $payloadSize + call $~lib/rt/tlsf/prepareBlock + i32.const 0 + drop + local.get $block + return + ) + (func $~lib/rt/tlsf/__alloc (param $size i32) (result i32) + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $size + call $~lib/rt/tlsf/allocateBlock + i32.const 4 + i32.add + return + ) + (func $~lib/rt/itcms/Object#set:rtId (param $this i32) (param $rtId i32) + local.get $this + local.get $rtId + i32.store offset=12 + ) + (func $~lib/rt/itcms/Object#set:rtSize (param $this i32) (param $rtSize i32) + local.get $this + local.get $rtSize + i32.store offset=16 + ) + (func $~lib/rt/itcms/__new (param $size i32) (param $id i32) (result i32) + (local $obj i32) + (local $ptr i32) + local.get $size + i32.const 1073741804 + i32.ge_u + if + i32.const 96 + i32.const 160 + i32.const 261 + i32.const 31 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.ge_u + if + call $~lib/rt/itcms/interrupt + end + i32.const 16 + local.get $size + i32.add + call $~lib/rt/tlsf/__alloc + i32.const 4 + i32.sub + local.set $obj + local.get $obj + local.get $id + call $~lib/rt/itcms/Object#set:rtId + local.get $obj + local.get $size + call $~lib/rt/itcms/Object#set:rtSize + local.get $obj + global.get $~lib/rt/itcms/fromSpace + global.get $~lib/rt/itcms/white + call $~lib/rt/itcms/Object#linkTo + global.get $~lib/rt/itcms/total + local.get $obj + call $~lib/rt/itcms/Object#get:size + i32.add + global.set $~lib/rt/itcms/total + local.get $obj + i32.const 20 + i32.add + local.set $ptr + local.get $ptr + i32.const 0 + local.get $size + memory.fill + local.get $ptr + return + ) + (func $features/tail-calls/maybeBox (param $x i32) (result i32) + local.get $x + if (result i32) + i32.const 0 + return_call $features/tail-calls/Box#constructor + else + i32.const 0 + end + return + ) + (func $features/tail-calls/nullable (param $x i32) (result i32) + local.get $x + return_call $features/tail-calls/maybeBox + ) + (func $features/tail-calls/maybeInvoke (param $fn i32) (param $x i32) (result i32) + local.get $x + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/recursive (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/recursive + ) + (func $features/tail-calls/mutualB (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/mutualA + ) + (func $features/tail-calls/mutualA (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/mutualB + ) + (func $features/tail-calls/ternary (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if (result i32) + i32.const 42 + else + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/ternary + end + return + ) + (func $features/tail-calls/temporary (param $n i32) (result i32) + (local $result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/temporary + ) + (func $features/tail-calls/comma (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + global.get $features/tail-calls/effects + return + end + global.get $features/tail-calls/effects + i32.const 1 + i32.add + global.set $features/tail-calls/effects + local.get $n + i32.const 1 + i32.sub + return_call $features/tail-calls/comma + ) + (func $features/tail-calls/switchCall (param $n i32) (result i32) + (local $1 i32) + block $case2|0 + block $case1|0 + block $case0|0 + local.get $n + local.set $1 + local.get $1 + i32.const 0 + i32.eq + br_if $case0|0 + local.get $1 + i32.const 1 + i32.eq + br_if $case1|0 + br $case2|0 + end + local.get $n + return_call $features/tail-calls/inc + end + local.get $n + i32.const 1 + i32.add + return_call $features/tail-calls/inc + end + local.get $n + i32.const 2 + i32.add + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/bothCalls (param $n i32) (result i32) + local.get $n + if + local.get $n + return_call $features/tail-calls/inc + else + i32.const 0 + return_call $features/tail-calls/inc + end + unreachable + ) + (func $features/tail-calls/nestedArguments (param $n i32) (result i32) + local.get $n + call $features/tail-calls/inc + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/branches (param $n i32) (result i32) + local.get $n + if + local.get $n + return_call $features/tail-calls/inc + else + i32.const 0 + return_call $features/tail-calls/inc + end + unreachable + ) + (func $features/tail-calls/recursiveIndirect (param $fn i32) (param $n i32) (result i32) + local.get $n + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/indirectStep (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + local.set $n + local.get $n + i32.const 1 + global.set $~argumentsLength + global.get $features/tail-calls/indirectFunction + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/temporaryIndirect (param $fn i32) (param $n i32) (result i32) + (local $result i32) + local.get $n + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/choose (result i32) + global.get $features/tail-calls/order + i32.const 10 + i32.mul + i32.const 1 + i32.add + global.set $features/tail-calls/order + i32.const 32 + return + ) + (func $features/tail-calls/argument (result i32) + global.get $features/tail-calls/order + i32.const 10 + i32.mul + i32.const 2 + i32.add + global.set $features/tail-calls/order + i32.const 40 + return + ) + (func $features/tail-calls/getOrder (result i32) + global.get $features/tail-calls/order + return + ) + (func $features/tail-calls/optional (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add + return + ) + (func $features/tail-calls/optionalDirect (param $x i32) (result i32) + local.get $x + i32.const 7 + return_call $features/tail-calls/optional + ) + (func $features/tail-calls/optionalIndirect (param $x i32) (result i32) + local.get $x + i32.const 0 + i32.const 1 + global.set $~argumentsLength + i32.const 560 + i32.load + return_call_indirect (type $2) + ) + (func $features/tail-calls/inlineCaller (param $x i32) (result i32) + (local $x|1 i32) + block $features/tail-calls/inlined|inlined.0 (result i32) + local.get $x + local.set $x|1 + local.get $x|1 + call $features/tail-calls/inc + br $features/tail-calls/inlined|inlined.0 + end + i32.const 1 + i32.add + return + ) + (func $features/tail-calls/wide (result i32) + i32.const 511 + return + ) + (func $features/tail-calls/narrowed (result i32) + call $features/tail-calls/wide + i32.const 255 + i32.and + return + ) + (func $features/tail-calls/small (param $x i32) (result i32) + local.get $x + return + ) + (func $features/tail-calls/wrapped (param $x i32) (result i32) + local.get $x + call $features/tail-calls/small + i32.const 255 + i32.and + return + ) + (func $features/tail-calls/discarded (param $x i32) + local.get $x + call $features/tail-calls/inc + drop + return + ) + (func $features/tail-calls/afterCall (param $x i32) (result i32) + (local $result i32) + local.get $x + call $features/tail-calls/inc + local.set $result + global.get $features/tail-calls/effects + i32.const 1 + i32.add + global.set $features/tail-calls/effects + local.get $result + return + ) + (func $features/tail-calls/nonTail (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 0 + return + end + local.get $n + i32.const 1 + i32.sub + call $features/tail-calls/nonTail + i32.const 1 + i32.add + return + ) + (func $features/tail-calls/voidCall + return_call $features/tail-calls/noop + ) + (func $features/tail-calls/voidImplicit + return_call $features/tail-calls/noop + ) + (func $features/tail-calls/Value#set:value (param $this i32) (param $value i32) + local.get $this + local.get $value + i32.store + ) + (func $~lib/rt/itcms/__collect + i32.const 0 + drop + global.get $~lib/rt/itcms/state + i32.const 0 + i32.gt_s + if + loop $while-continue|0 + global.get $~lib/rt/itcms/state + i32.const 0 + i32.ne + if + call $~lib/rt/itcms/step + drop + br $while-continue|0 + end + end + end + call $~lib/rt/itcms/step + drop + loop $while-continue|1 + global.get $~lib/rt/itcms/state + i32.const 0 + i32.ne + if + call $~lib/rt/itcms/step + drop + br $while-continue|1 + end + end + global.get $~lib/rt/itcms/total + i64.extend_i32_u + i32.const 200 + i64.extend_i32_u + i64.mul + i64.const 100 + i64.div_u + i32.wrap_i64 + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + i32.const 0 + drop + i32.const 0 + drop + ) + (func $features/tail-calls/Value#get:value (param $this i32) (result i32) + local.get $this + i32.load + ) + (func $features/tail-calls/stackPointer (result i32) + global.get $~lib/memory/__stack_pointer + return + ) + (func $features/tail-calls/callArrow (param $x i32) (result i32) + local.get $x + i32.const 1 + global.set $~argumentsLength + global.get $features/tail-calls/arrow + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/Base#value (param $this i32) (param $x i32) (result i32) + local.get $x + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/rawStep (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.eq + if + i32.const 42 + return + end + local.get $n + i32.const 1 + i32.sub + global.get $features/tail-calls/indirectIndex + return_call_indirect (type $0) + ) + (func $~lib/function/Function<%28i32%29=>i32>#get:index (param $this i32) (result i32) + local.get $this + i32.load + return + ) + (func $features/tail-calls/imported (param $x i32) (result i32) + local.get $x + return_call $features/tail-calls/host + ) + (func $features/tail-calls/optional@varargs (param $x i32) (param $y i32) (result i32) + block $1of1 + block $0of1 + block $outOfRange + global.get $~argumentsLength + i32.const 1 + i32.sub + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + i32.const 7 + local.set $y + end + local.get $x + local.get $y + return_call $features/tail-calls/optional + ) + (func $features/tail-calls/Derived#value (param $this i32) (param $x i32) (result i32) + local.get $x + i32.const 1 + i32.add + return_call $features/tail-calls/inc + ) + (func $features/tail-calls/Base#value@override (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + block $default + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $2 + local.get $2 + i32.const 10 + i32.eq + br_if $case0 + br $default + end + local.get $0 + local.get $1 + return_call $features/tail-calls/Derived#value + end + local.get $0 + local.get $1 + return_call $features/tail-calls/Base#value + ) + (func $~lib/rt/__visit_globals (param $0 i32) + (local $1 i32) + i32.const 288 + local.get $0 + call $~lib/rt/itcms/__visit + i32.const 96 + local.get $0 + return_call $~lib/rt/itcms/__visit + ) + (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $0 + local.get $1 + call $~lib/object/Object~visit + local.get $0 + i32.load + local.get $1 + return_call $~lib/rt/itcms/__visit + ) + (func $~lib/object/Object~visit (param $0 i32) (param $1 i32) + ) + (func $~lib/function/Function<%28i32%29=>i32>#get:_env (param $this i32) (result i32) + local.get $this + i32.load offset=4 + ) + (func $~lib/function/Function<%28i32%29=>i32>~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/object/Object~visit + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%29=>i32>#__visit + ) + (func $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>#get:_env (param $this i32) (result i32) + local.get $this + i32.load offset=4 + ) + (func $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/object/Object~visit + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>#__visit + ) + (func $~lib/function/Function<%28i32%2Ci32?%29=>i32>#get:_env (param $this i32) (result i32) + local.get $this + i32.load offset=4 + ) + (func $~lib/function/Function<%28i32%2Ci32?%29=>i32>~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/object/Object~visit + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%2Ci32?%29=>i32>#__visit + ) + (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) + block $invalid + block $features/tail-calls/Derived + block $features/tail-calls/Base + block $features/tail-calls/Value + block $~lib/function/Function<%28i32%2Ci32?%29=>i32> + block $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null> + block $features/tail-calls/Box + block $~lib/function/Function<%28i32%29=>i32> + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + block $~lib/object/Object + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/function/Function<%28i32%29=>i32> $features/tail-calls/Box $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null> $~lib/function/Function<%28i32%2Ci32?%29=>i32> $features/tail-calls/Value $features/tail-calls/Base $features/tail-calls/Derived $invalid + end + return + end + return + end + return + end + local.get $0 + local.get $1 + return_call $~lib/arraybuffer/ArrayBufferView~visit + end + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%29=>i32>~visit + end + return + end + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>~visit + end + local.get $0 + local.get $1 + return_call $~lib/function/Function<%28i32%2Ci32?%29=>i32>~visit + end + return + end + return + end + return + end + unreachable + ) + (func $~start + memory.size + i32.const 16 + i32.shl + global.get $~lib/memory/__heap_base + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 208 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/pinSpace + i32.const 240 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/toSpace + i32.const 384 + call $~lib/rt/itcms/initLazy + global.set $~lib/rt/itcms/fromSpace + ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 33648 + i32.const 33696 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $features/tail-calls/indirect (param $x i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 32 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $x + call $features/tail-calls/invoke + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/convertedIndirect (param $x i32) (result i64) + (local $1 i32) + (local $2 i64) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 32 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $x + call $features/tail-calls/widenInvoke + local.set $2 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $2 + return + ) + (func $features/tail-calls/Box#constructor (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 5 + call $~lib/rt/itcms/__new + local.tee $this + i32.store + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $~lib/object/Object#constructor + local.tee $this + i32.store + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $features/tail-calls/nullableIndirect (param $x i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 496 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $x + call $features/tail-calls/maybeInvoke + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/deepIndirect (param $n i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 528 + global.set $features/tail-calls/indirectFunction + i32.const 528 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $n + call $features/tail-calls/recursiveIndirect + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/callTemporaryIndirect (param $n i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 32 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $n + call $features/tail-calls/temporaryIndirect + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/Value#constructor (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.const 8 + call $~lib/rt/itcms/__new + local.tee $this + i32.store + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $~lib/object/Object#constructor + local.tee $this + i32.store + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + i32.const 42 + call $features/tail-calls/Value#set:value + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $features/tail-calls/readValue (param $value i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + call $~lib/rt/itcms/__collect + local.get $value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + call $features/tail-calls/Value#get:value + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/managed (result i32) + (local $value i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + i32.const 0 + call $features/tail-calls/Value#constructor + local.tee $value + i32.store + local.get $value + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $features/tail-calls/readValue + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/Base#constructor (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 9 + call $~lib/rt/itcms/__new + local.tee $this + i32.store + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $~lib/object/Object#constructor + local.tee $this + i32.store + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $features/tail-calls/Derived#constructor (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 10 + call $~lib/rt/itcms/__new + local.tee $this + i32.store + end + global.get $~lib/memory/__stack_pointer + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $features/tail-calls/Base#constructor + local.tee $this + i32.store + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $features/tail-calls/dispatch (param $value i32) (param $x i32) (result i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $value + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store + local.get $2 + local.get $x + call $features/tail-calls/Base#value@override + local.set $2 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $2 + return + ) + (func $features/tail-calls/virtual (param $x i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 0 + call $features/tail-calls/Derived#constructor + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + local.get $x + call $features/tail-calls/dispatch + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/deepRaw (param $n i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 784 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + call $~lib/function/Function<%28i32%29=>i32>#get:index + global.set $features/tail-calls/indirectIndex + local.get $n + call $features/tail-calls/rawStep + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $~lib/function/Function<%28i32%29=>i32>#__visit (param $this i32) (param $cookie i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $this + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store + local.get $2 + call $~lib/function/Function<%28i32%29=>i32>#get:_env + local.get $cookie + call $~lib/rt/itcms/__visit + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>#__visit (param $this i32) (param $cookie i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $this + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store + local.get $2 + call $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null>#get:_env + local.get $cookie + call $~lib/rt/itcms/__visit + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $~lib/function/Function<%28i32%2Ci32?%29=>i32>#__visit (param $this i32) (param $cookie i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $this + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store + local.get $2 + call $~lib/function/Function<%28i32%2Ci32?%29=>i32>#get:_env + local.get $cookie + call $~lib/rt/itcms/__visit + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $~lib/object/Object#constructor (param $this i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $this + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 0 + call $~lib/rt/itcms/__new + local.tee $this + i32.store + end + local.get $this + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) + (func $features/tail-calls/indirectOrder (result i32) + (local $0 i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 0 + global.set $features/tail-calls/order + global.get $~lib/memory/__stack_pointer + call $features/tail-calls/choose + local.tee $0 + i32.store + call $features/tail-calls/argument + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.load + call_indirect (type $0) + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + return + ) + (func $features/tail-calls/nonNull (param $x i32) (result i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $x + call $features/tail-calls/maybeBox + local.tee $1 + i32.store + local.get $1 + if (result i32) + local.get $1 + else + i32.const 592 + i32.const 720 + i32.const 178 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + local.set $2 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $2 + return + ) + (func $export:features/tail-calls/recursiveIndirect (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + local.get $1 + call $features/tail-calls/recursiveIndirect + local.set $2 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $2 + ) + (func $export:features/tail-calls/temporaryIndirect (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + local.get $1 + call $features/tail-calls/temporaryIndirect + local.set $2 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $2 + ) +) diff --git a/tests/compiler/features/tail-calls.js b/tests/compiler/features/tail-calls.js new file mode 100644 index 0000000000..8a2bd82ace --- /dev/null +++ b/tests/compiler/features/tail-calls.js @@ -0,0 +1,53 @@ +import assert from "node:assert/strict"; + +export function preInstantiate(imports) { + imports.env.tailHost = x => x + 2; +} + +export function postInstantiate({ exports: e }) { + assert.equal(e.direct(40), 41); + assert.equal(e.indirect(40), 41); + assert.equal(e.converted(40), 41n); + assert.equal(e.convertedIndirect(40), 41n); + assert.equal(e.nullable(0), 0); + assert.notEqual(e.nullable(1), 0); + assert.equal(e.nullableIndirect(0), 0); + assert.notEqual(e.nullableIndirect(1), 0); + + // These exceed the ordinary Wasm call stack in debug as well as release. + for (const name of ["recursive", "mutualA", "ternary", "temporary", "deepIndirect", "deepRaw"]) { + assert.equal(e[name](100000), 42, name); + } + assert.equal(e.comma(100000), 100000); + assert.equal(e.switchCall(0), 1); + assert.equal(e.switchCall(1), 3); + assert.equal(e.switchCall(5), 8); + assert.equal(e.bothCalls(0), 1); + assert.equal(e.bothCalls(40), 41); + assert.equal(e.nestedArguments(40), 42); + assert.equal(e.branches(0), 1); + assert.equal(e.branches(4), 5); + assert.equal(e.callTemporaryIndirect(40), 41); + assert.equal(e.indirectOrder(), 41); + assert.equal(e.getOrder(), 12); + assert.equal(e.optionalDirect(3), 10); + assert.equal(e.optionalIndirect(3), 10); + assert.equal(e.inlineCaller(40), 42); + assert.equal(e.narrowed(), 255); + assert.equal(e.wrapped(511), 255); + assert.equal(e.discarded(40), undefined); + assert.equal(e.afterCall(40), 41); + assert.equal(e.comma(0), 100001); + assert.equal(e.nonTail(10), 10); + assert.notEqual(e.nonNull(1), 0); + assert.throws(() => e.nonNull(0), WebAssembly.RuntimeError); + assert.equal(e.voidCall(), undefined); + assert.equal(e.voidImplicit(), undefined); + assert.equal(e.callArrow(40), 41); + assert.equal(e.virtual(40), 42); + assert.equal(e.imported(40), 42); + + const stackPointer = e.stackPointer(); + for (let i = 0; i < 100; ++i) assert.equal(e.managed(), 42); + assert.equal(e.stackPointer(), stackPointer); +} diff --git a/tests/compiler/features/tail-calls.json b/tests/compiler/features/tail-calls.json new file mode 100644 index 0000000000..9b3990402e --- /dev/null +++ b/tests/compiler/features/tail-calls.json @@ -0,0 +1,5 @@ +{ + "features": [ + "tail-calls" + ] +} diff --git a/tests/compiler/features/tail-calls.release.wat b/tests/compiler/features/tail-calls.release.wat new file mode 100644 index 0000000000..6b111ddee3 --- /dev/null +++ b/tests/compiler/features/tail-calls.release.wat @@ -0,0 +1,2557 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (result i32))) + (type $2 (func (param i32 i32) (result i32))) + (type $3 (func (param i32))) + (type $4 (func)) + (type $5 (func (param i32 i32))) + (type $6 (func (param i32) (result i64))) + (type $7 (func (param i32 i32 i32 i32))) + (type $8 (func (param i32 i32 i64))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (import "env" "tailHost" (func $features/tail-calls/host (param i32) (result i32))) + (global $features/tail-calls/effects (mut i32) (i32.const 0)) + (global $features/tail-calls/indirectFunction (mut i32) (i32.const 1056)) + (global $features/tail-calls/order (mut i32) (i32.const 0)) + (global $features/tail-calls/indirectIndex (mut i32) (i32.const 0)) + (global $~argumentsLength (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/iter (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) + (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 34640)) + (memory $0 1) + (data $0 (i32.const 1036) "\1c") + (data $0.1 (i32.const 1048) "\04\00\00\00\08\00\00\00\01") + (data $1 (i32.const 1068) "\1c") + (data $1.1 (i32.const 1080) "\04\00\00\00\08\00\00\00\02") + (data $2 (i32.const 1100) "<") + (data $2.1 (i32.const 1112) "\02\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") + (data $3 (i32.const 1164) "<") + (data $3.1 (i32.const 1176) "\02\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s") + (data $6 (i32.const 1292) "<") + (data $6.1 (i32.const 1304) "\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") + (data $7 (i32.const 1356) ",") + (data $7.1 (i32.const 1368) "\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") + (data $9 (i32.const 1436) "<") + (data $9.1 (i32.const 1448) "\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") + (data $10 (i32.const 1500) "\1c") + (data $10.1 (i32.const 1512) "\06\00\00\00\08\00\00\00\03") + (data $11 (i32.const 1532) "\1c") + (data $11.1 (i32.const 1544) "\04\00\00\00\08\00\00\00\04") + (data $12 (i32.const 1564) "\1c") + (data $12.1 (i32.const 1576) "\07\00\00\00\08\00\00\00\05") + (data $13 (i32.const 1596) "|") + (data $13.1 (i32.const 1608) "\02\00\00\00^\00\00\00U\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00\'\00n\00u\00l\00l\00\'\00 \00(\00n\00o\00t\00 \00a\00s\00s\00i\00g\00n\00e\00d\00 \00o\00r\00 \00f\00a\00i\00l\00e\00d\00 \00c\00a\00s\00t\00)") + (data $14 (i32.const 1724) "<") + (data $14.1 (i32.const 1736) "\02\00\00\00,\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00t\00a\00i\00l\00-\00c\00a\00l\00l\00s\00.\00t\00s") + (data $15 (i32.const 1788) "\1c") + (data $15.1 (i32.const 1800) "\04\00\00\00\08\00\00\00\06") + (data $16 (i32.const 1824) "\0b\00\00\00 \00\00\00 \00\00\00 ") + (data $16.1 (i32.const 1848) " ") + (data $16.2 (i32.const 1860) " \00\00\00 \00\00\00 ") + (table $0 7 7 funcref) + (elem $0 (i32.const 1) $start:features/tail-calls~anonymous|0 $start:features/tail-calls~anonymous|0 $features/tail-calls/maybeBox $features/tail-calls/indirectStep $features/tail-calls/optional@varargs $features/tail-calls/rawStep) + (export "direct" (func $start:features/tail-calls~anonymous|0)) + (export "directVoid" (func $features/tail-calls/directVoid)) + (export "indirect" (func $features/tail-calls/indirect)) + (export "converted" (func $features/tail-calls/converted)) + (export "convertedIndirect" (func $features/tail-calls/convertedIndirect)) + (export "nullable" (func $features/tail-calls/nullable)) + (export "nullableIndirect" (func $features/tail-calls/nullableIndirect)) + (export "recursive" (func $features/tail-calls/recursive)) + (export "mutualA" (func $features/tail-calls/mutualA)) + (export "ternary" (func $features/tail-calls/ternary)) + (export "temporary" (func $features/tail-calls/temporary)) + (export "comma" (func $features/tail-calls/comma)) + (export "switchCall" (func $features/tail-calls/switchCall)) + (export "bothCalls" (func $features/tail-calls/bothCalls)) + (export "nestedArguments" (func $features/tail-calls/nestedArguments)) + (export "branches" (func $features/tail-calls/bothCalls)) + (export "deepIndirect" (func $features/tail-calls/deepIndirect)) + (export "callTemporaryIndirect" (func $features/tail-calls/indirect)) + (export "indirectOrder" (func $features/tail-calls/indirectOrder)) + (export "getOrder" (func $features/tail-calls/getOrder)) + (export "optionalDirect" (func $features/tail-calls/optionalDirect)) + (export "optionalIndirect" (func $features/tail-calls/optionalIndirect)) + (export "inlineCaller" (func $features/tail-calls/nestedArguments)) + (export "narrowed" (func $features/tail-calls/narrowed)) + (export "wrapped" (func $features/tail-calls/wrapped)) + (export "discarded" (func $features/tail-calls/discarded)) + (export "afterCall" (func $features/tail-calls/afterCall)) + (export "nonTail" (func $features/tail-calls/nonTail)) + (export "nonNull" (func $features/tail-calls/nonNull)) + (export "voidCall" (func $features/tail-calls/directVoid)) + (export "voidImplicit" (func $features/tail-calls/directVoid)) + (export "managed" (func $features/tail-calls/managed)) + (export "stackPointer" (func $features/tail-calls/stackPointer)) + (export "callArrow" (func $features/tail-calls/callArrow)) + (export "virtual" (func $features/tail-calls/virtual)) + (export "deepRaw" (func $features/tail-calls/deepRaw)) + (export "imported" (func $features/tail-calls/imported)) + (export "memory" (memory $0)) + (export "recursiveIndirect" (func $export:features/tail-calls/temporaryIndirect)) + (export "temporaryIndirect" (func $export:features/tail-calls/temporaryIndirect)) + (start $~start) + (func $~lib/rt/itcms/__visit (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.eqz + if + return + end + global.get $~lib/rt/itcms/white + local.get $0 + i32.const 20 + i32.sub + local.tee $1 + i32.load offset=4 + i32.const 3 + i32.and + i32.eq + if + local.get $1 + global.get $~lib/rt/itcms/iter + i32.eq + if + local.get $1 + i32.load offset=8 + local.tee $0 + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 148 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + global.set $~lib/rt/itcms/iter + end + block $__inlined_func$~lib/rt/itcms/Object#unlink$171 + local.get $1 + i32.load offset=4 + i32.const -4 + i32.and + local.tee $0 + i32.eqz + if + local.get $1 + i32.load offset=8 + i32.eqz + local.get $1 + i32.const 34640 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 128 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + br $__inlined_func$~lib/rt/itcms/Object#unlink$171 + end + local.get $1 + i32.load offset=8 + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 132 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.store offset=8 + local.get $2 + local.get $0 + local.get $2 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + end + global.get $~lib/rt/itcms/toSpace + local.set $2 + local.get $1 + i32.load offset=12 + local.tee $0 + i32.const 2 + i32.le_u + if (result i32) + i32.const 1 + else + local.get $0 + i32.const 1824 + i32.load + i32.gt_u + if + i32.const 1312 + i32.const 1376 + i32.const 21 + i32.const 28 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 2 + i32.shl + i32.const 1828 + i32.add + i32.load + i32.const 32 + i32.and + end + local.set $3 + local.get $2 + i32.load offset=8 + local.set $0 + local.get $1 + global.get $~lib/rt/itcms/white + i32.eqz + i32.const 2 + local.get $3 + select + local.get $2 + i32.or + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + local.get $0 + local.get $1 + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + local.get $2 + local.get $1 + i32.store offset=8 + global.get $~lib/rt/itcms/visitCount + i32.const 1 + i32.add + global.set $~lib/rt/itcms/visitCount + end + ) + (func $~lib/rt/itcms/__new (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 $0 + i32.const 1073741804 + i32.ge_u + if + i32.const 1120 + i32.const 1184 + i32.const 261 + i32.const 31 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.ge_u + if + block $__inlined_func$~lib/rt/itcms/interrupt$72 + i32.const 2048 + local.set $2 + loop $do-loop|0 + local.get $2 + call $~lib/rt/itcms/step + i32.sub + local.set $2 + global.get $~lib/rt/itcms/state + i32.eqz + if + global.get $~lib/rt/itcms/total + i32.const 1 + i32.shl + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + br $__inlined_func$~lib/rt/itcms/interrupt$72 + end + local.get $2 + i32.const 0 + i32.gt_s + br_if $do-loop|0 + end + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/total + global.get $~lib/rt/itcms/threshold + i32.sub + i32.const 1024 + i32.lt_u + i32.const 10 + i32.shl + i32.add + global.set $~lib/rt/itcms/threshold + end + end + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.set $4 + local.get $0 + i32.const 16 + i32.add + local.tee $2 + i32.const 1073741820 + i32.gt_u + if + i32.const 1120 + i32.const 1456 + i32.const 461 + i32.const 29 + call $~lib/builtins/abort + unreachable + end + local.get $4 + local.get $2 + i32.const 12 + i32.le_u + if (result i32) + i32.const 12 + else + local.get $2 + i32.const 19 + i32.add + i32.const -16 + i32.and + i32.const 4 + i32.sub + end + local.tee $5 + call $~lib/rt/tlsf/searchBlock + local.tee $2 + i32.eqz + if + local.get $5 + i32.const 256 + i32.ge_u + if (result i32) + local.get $5 + i32.const 536870910 + i32.lt_u + if (result i32) + local.get $5 + i32.const 1 + i32.const 27 + local.get $5 + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + else + local.get $5 + end + else + local.get $5 + end + i32.const 4 + local.get $4 + i32.load offset=1568 + memory.size + local.tee $2 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + i32.ne + i32.shl + i32.add + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $2 + local.get $3 + local.get $2 + local.get $3 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $3 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + local.get $4 + local.get $2 + i32.const 16 + i32.shl + memory.size + i64.extend_i32_s + i64.const 16 + i64.shl + call $~lib/rt/tlsf/addMemory + local.get $4 + local.get $5 + call $~lib/rt/tlsf/searchBlock + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 499 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + local.get $5 + local.get $2 + i32.load + i32.const -4 + i32.and + i32.gt_u + if + i32.const 0 + i32.const 1456 + i32.const 501 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + local.get $2 + call $~lib/rt/tlsf/removeBlock + local.get $2 + i32.load + local.set $6 + local.get $5 + i32.const 4 + i32.add + i32.const 15 + i32.and + if + i32.const 0 + i32.const 1456 + i32.const 361 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $6 + i32.const -4 + i32.and + local.get $5 + i32.sub + local.tee $3 + i32.const 16 + i32.ge_u + if + local.get $2 + local.get $5 + local.get $6 + i32.const 2 + i32.and + i32.or + i32.store + local.get $2 + i32.const 4 + i32.add + local.get $5 + i32.add + local.tee $5 + local.get $3 + i32.const 4 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $4 + local.get $5 + call $~lib/rt/tlsf/insertBlock + else + local.get $2 + local.get $6 + i32.const -2 + i32.and + i32.store + local.get $2 + i32.const 4 + i32.add + local.get $2 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $3 + local.get $3 + i32.load + i32.const -3 + i32.and + i32.store + end + local.get $2 + local.get $1 + i32.store offset=12 + local.get $2 + local.get $0 + i32.store offset=16 + global.get $~lib/rt/itcms/fromSpace + local.tee $1 + i32.load offset=8 + local.set $3 + local.get $2 + local.get $1 + global.get $~lib/rt/itcms/white + i32.or + i32.store offset=4 + local.get $2 + local.get $3 + i32.store offset=8 + local.get $3 + local.get $2 + local.get $3 + i32.load offset=4 + i32.const 3 + i32.and + i32.or + i32.store offset=4 + local.get $1 + local.get $2 + i32.store offset=8 + global.get $~lib/rt/itcms/total + local.get $2 + i32.load + i32.const -4 + i32.and + i32.const 4 + i32.add + i32.add + global.set $~lib/rt/itcms/total + local.get $2 + i32.const 20 + i32.add + local.tee $1 + i32.const 0 + local.get $0 + memory.fill + local.get $1 + ) + (func $~lib/rt/itcms/step (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + block $break|0 + block $case2|0 + block $case1|0 + block $case0|0 + global.get $~lib/rt/itcms/state + br_table $case0|0 $case1|0 $case2|0 $break|0 + end + i32.const 1 + global.set $~lib/rt/itcms/state + i32.const 0 + global.set $~lib/rt/itcms/visitCount + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/visitCount + return + end + global.get $~lib/rt/itcms/white + i32.eqz + local.set $1 + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|1 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $0 + global.set $~lib/rt/itcms/iter + local.get $1 + local.get $0 + i32.load offset=4 + local.tee $2 + i32.const 3 + i32.and + i32.ne + if + local.get $0 + local.get $2 + i32.const -4 + i32.and + local.get $1 + i32.or + i32.store offset=4 + i32.const 0 + global.set $~lib/rt/itcms/visitCount + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + global.get $~lib/rt/itcms/visitCount + return + end + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|1 + end + end + i32.const 0 + global.set $~lib/rt/itcms/visitCount + call $~lib/rt/itcms/visitRoots + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + i32.eq + if + global.get $~lib/memory/__stack_pointer + local.set $0 + loop $while-continue|0 + local.get $0 + i32.const 34640 + i32.lt_u + if + local.get $0 + i32.load + call $~lib/rt/itcms/__visit + local.get $0 + i32.const 4 + i32.add + local.set $0 + br $while-continue|0 + end + end + global.get $~lib/rt/itcms/iter + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|2 + local.get $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $1 + local.get $0 + i32.load offset=4 + local.tee $2 + i32.const 3 + i32.and + i32.ne + if + local.get $0 + local.get $2 + i32.const -4 + i32.and + local.get $1 + i32.or + i32.store offset=4 + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + end + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|2 + end + end + global.get $~lib/rt/itcms/fromSpace + local.set $0 + global.get $~lib/rt/itcms/toSpace + global.set $~lib/rt/itcms/fromSpace + local.get $0 + global.set $~lib/rt/itcms/toSpace + local.get $1 + global.set $~lib/rt/itcms/white + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + global.set $~lib/rt/itcms/iter + i32.const 2 + global.set $~lib/rt/itcms/state + end + global.get $~lib/rt/itcms/visitCount + return + end + global.get $~lib/rt/itcms/iter + local.tee $0 + global.get $~lib/rt/itcms/toSpace + i32.ne + if + local.get $0 + i32.load offset=4 + local.tee $1 + i32.const -4 + i32.and + global.set $~lib/rt/itcms/iter + global.get $~lib/rt/itcms/white + i32.eqz + local.get $1 + i32.const 3 + i32.and + i32.ne + if + i32.const 0 + i32.const 1184 + i32.const 229 + i32.const 20 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 34640 + i32.lt_u + if + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + else + global.get $~lib/rt/itcms/total + local.get $0 + i32.load + i32.const -4 + i32.and + i32.const 4 + i32.add + i32.sub + global.set $~lib/rt/itcms/total + local.get $0 + i32.const 4 + i32.add + local.tee $0 + i32.const 34640 + i32.ge_u + if + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + i32.const 4 + i32.sub + local.set $2 + local.get $0 + i32.const 15 + i32.and + i32.const 1 + local.get $0 + select + if (result i32) + i32.const 1 + else + local.get $2 + i32.load + i32.const 1 + i32.and + end + if + i32.const 0 + i32.const 1456 + i32.const 562 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $2 + local.get $2 + i32.load + i32.const 1 + i32.or + i32.store + local.get $2 + call $~lib/rt/tlsf/insertBlock + end + end + i32.const 10 + return + end + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + i32.store offset=4 + global.get $~lib/rt/itcms/toSpace + global.get $~lib/rt/itcms/toSpace + i32.store offset=8 + i32.const 0 + global.set $~lib/rt/itcms/state + end + i32.const 0 + ) + (func $~lib/rt/tlsf/removeBlock (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 1456 + i32.const 268 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const -4 + i32.and + local.tee $3 + i32.const 12 + i32.lt_u + if + i32.const 0 + i32.const 1456 + i32.const 270 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 256 + i32.lt_u + if (result i32) + local.get $3 + i32.const 4 + i32.shr_u + else + i32.const 31 + i32.const 1073741820 + local.get $3 + local.get $3 + i32.const 1073741820 + i32.ge_u + select + local.tee $3 + i32.clz + i32.sub + local.tee $4 + i32.const 7 + i32.sub + local.set $2 + local.get $3 + local.get $4 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + end + local.tee $3 + i32.const 16 + i32.lt_u + local.get $2 + i32.const 23 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 284 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load offset=8 + local.set $5 + local.get $1 + i32.load offset=4 + local.tee $4 + if + local.get $4 + local.get $5 + i32.store offset=8 + end + local.get $5 + if + local.get $5 + local.get $4 + i32.store offset=4 + end + local.get $1 + local.get $0 + local.get $2 + i32.const 4 + i32.shl + local.get $3 + i32.add + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load offset=96 + i32.eq + if + local.get $1 + local.get $5 + i32.store offset=96 + local.get $5 + i32.eqz + if + local.get $0 + local.get $2 + i32.const 2 + i32.shl + i32.add + local.tee $1 + i32.load offset=4 + i32.const -2 + local.get $3 + i32.rotl + i32.and + local.set $3 + local.get $1 + local.get $3 + i32.store offset=4 + local.get $3 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const -2 + local.get $2 + i32.rotl + i32.and + i32.store + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 201 + i32.const 14 + 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 1456 + i32.const 203 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $4 + i32.load + local.tee $2 + i32.const 1 + i32.and + if + local.get $0 + local.get $4 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $3 + i32.const 4 + i32.add + local.get $2 + i32.const -4 + i32.and + i32.add + local.tee $3 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $4 + i32.load + local.set $2 + end + local.get $3 + i32.const 2 + i32.and + if + local.get $1 + i32.const 4 + i32.sub + i32.load + local.tee $1 + i32.load + local.tee $6 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 221 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $6 + i32.const 4 + i32.add + local.get $3 + i32.const -4 + i32.and + i32.add + local.tee $3 + i32.store + end + local.get $4 + local.get $2 + i32.const 2 + i32.or + i32.store + local.get $3 + i32.const -4 + i32.and + local.tee $2 + i32.const 12 + i32.lt_u + if + i32.const 0 + i32.const 1456 + i32.const 233 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + local.get $1 + i32.const 4 + i32.add + local.get $2 + i32.add + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 234 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $2 + i32.const 256 + i32.lt_u + if (result i32) + local.get $2 + i32.const 4 + i32.shr_u + else + i32.const 31 + i32.const 1073741820 + local.get $2 + local.get $2 + i32.const 1073741820 + i32.ge_u + select + local.tee $2 + i32.clz + i32.sub + local.tee $3 + i32.const 7 + i32.sub + local.set $5 + local.get $2 + local.get $3 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + end + local.tee $2 + i32.const 16 + i32.lt_u + local.get $5 + i32.const 23 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 251 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $5 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $3 + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + local.get $3 + i32.store offset=8 + local.get $3 + if + local.get $3 + local.get $1 + i32.store offset=4 + end + local.get $0 + local.get $5 + i32.const 4 + i32.shl + local.get $2 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store offset=96 + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $5 + i32.shl + i32.or + i32.store + local.get $0 + local.get $5 + i32.const 2 + i32.shl + i32.add + local.tee $0 + local.get $0 + i32.load offset=4 + i32.const 1 + local.get $2 + i32.shl + i32.or + i32.store offset=4 + ) + (func $~lib/rt/__visit_members (param $0 i32) + block $invalid + block $features/tail-calls/Derived + block $features/tail-calls/Base + block $features/tail-calls/Value + block $~lib/function/Function<%28i32%2Ci32?%29=>i32> + block $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null> + block $features/tail-calls/Box + block $~lib/function/Function<%28i32%29=>i32> + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + block $~lib/object/Object + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/function/Function<%28i32%29=>i32> $features/tail-calls/Box $~lib/function/Function<%28i32%29=>features/tail-calls/Box|null> $~lib/function/Function<%28i32%2Ci32?%29=>i32> $features/tail-calls/Value $features/tail-calls/Base $features/tail-calls/Derived $invalid + end + return + end + return + end + return + end + local.get $0 + i32.load + return_call $~lib/rt/itcms/__visit + end + local.get $0 + return_call $~lib/function/Function<%28i32%29=>i32>~visit + end + return + end + local.get $0 + return_call $~lib/function/Function<%28i32%29=>i32>~visit + end + local.get $0 + return_call $~lib/function/Function<%28i32%29=>i32>~visit + end + return + end + return + end + return + end + unreachable + ) + (func $~lib/object/Object#constructor (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $0 + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 0 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + ) + (func $~lib/function/Function<%28i32%29=>i32>~visit (param $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + i32.load offset=4 + call $~lib/rt/itcms/__visit + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/maybeBox (param $0 i32) (result i32) + local.get $0 + if + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 5 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store offset=4 + global.get $~lib/memory/__stack_pointer + local.get $0 + call $~lib/object/Object#constructor + local.tee $0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + else + i32.const 0 + local.set $0 + end + local.get $0 + ) + (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + i32.const 256 + i32.lt_u + if + local.get $1 + i32.const 4 + i32.shr_u + local.set $1 + else + local.get $1 + i32.const 536870910 + i32.lt_u + if + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + local.set $1 + end + local.get $1 + i32.const 31 + local.get $1 + i32.clz + i32.sub + local.tee $2 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + local.set $1 + local.get $2 + i32.const 7 + i32.sub + local.set $2 + end + local.get $1 + i32.const 16 + i32.lt_u + local.get $2 + i32.const 23 + i32.lt_u + i32.and + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 334 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const -1 + local.get $1 + i32.shl + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + i32.ctz + local.get $2 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + 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 + i32.ctz + local.tee $1 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 1456 + i32.const 347 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.ctz + local.get $1 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + else + i32.const 0 + end + end + ) + (func $~lib/rt/tlsf/initialize + (local $0 i32) + (local $1 i32) + memory.size + local.tee $1 + i32.const 0 + i32.le_s + if (result i32) + i32.const 1 + local.get $1 + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + i32.const 34640 + i32.const 0 + i32.store + i32.const 36208 + i32.const 0 + i32.store + loop $for-loop|0 + local.get $0 + i32.const 23 + i32.lt_u + if + local.get $0 + i32.const 2 + i32.shl + i32.const 34640 + i32.add + i32.const 0 + i32.store offset=4 + i32.const 0 + local.set $1 + loop $for-loop|1 + local.get $1 + i32.const 16 + i32.lt_u + if + local.get $0 + i32.const 4 + i32.shl + local.get $1 + i32.add + i32.const 2 + i32.shl + i32.const 34640 + i32.add + i32.const 0 + i32.store offset=96 + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|1 + end + end + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|0 + end + end + i32.const 34640 + i32.const 36212 + memory.size + i64.extend_i32_s + i64.const 16 + i64.shl + call $~lib/rt/tlsf/addMemory + i32.const 34640 + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i64) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $2 + local.get $1 + i64.extend_i32_u + i64.lt_u + if + i32.const 0 + i32.const 1456 + i32.const 382 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 19 + i32.add + i32.const -16 + i32.and + i32.const 4 + i32.sub + local.set $1 + local.get $0 + i32.load offset=1568 + local.tee $3 + if + local.get $3 + i32.const 4 + i32.add + local.get $1 + i32.gt_u + if + i32.const 0 + i32.const 1456 + i32.const 389 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $3 + local.get $1 + i32.const 16 + i32.sub + local.tee $5 + i32.eq + if + local.get $3 + i32.load + local.set $4 + local.get $5 + local.set $1 + end + else + local.get $0 + i32.const 1572 + i32.add + local.get $1 + i32.gt_u + if + i32.const 0 + i32.const 1456 + i32.const 402 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + i32.wrap_i64 + i32.const -16 + i32.and + local.get $1 + i32.sub + local.tee $3 + i32.const 20 + i32.lt_u + if + return + end + local.get $1 + local.get $4 + i32.const 2 + i32.and + local.get $3 + i32.const 8 + i32.sub + local.tee $3 + i32.const 1 + i32.or + i32.or + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 4 + i32.add + local.get $3 + i32.add + local.tee $3 + i32.const 2 + i32.store + local.get $0 + local.get $3 + i32.store offset=1568 + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/itcms/visitRoots + (local $0 i32) + (local $1 i32) + i32.const 1312 + call $~lib/rt/itcms/__visit + i32.const 1120 + call $~lib/rt/itcms/__visit + global.get $~lib/rt/itcms/pinSpace + local.tee $1 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + loop $while-continue|0 + local.get $0 + local.get $1 + i32.ne + if + local.get $0 + i32.load offset=4 + i32.const 3 + i32.and + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1184 + i32.const 160 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 20 + i32.add + call $~lib/rt/__visit_members + local.get $0 + i32.load offset=4 + i32.const -4 + i32.and + local.set $0 + br $while-continue|0 + end + end + ) + (func $start:features/tail-calls~anonymous|0 (param $0 i32) (result i32) + local.get $0 + i32.const 1 + i32.add + ) + (func $features/tail-calls/ternary (param $0 i32) (result i32) + local.get $0 + if + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/ternary + end + i32.const 42 + ) + (func $features/tail-calls/temporary (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/temporary + ) + (func $features/tail-calls/recursive (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/recursive + ) + (func $features/tail-calls/nonTail (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + return + end + local.get $0 + i32.const 1 + i32.sub + call $features/tail-calls/nonTail + i32.const 1 + i32.add + ) + (func $features/tail-calls/mutualB (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + local.get $0 + i32.const 1 + i32.sub + local.tee $0 + if + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/mutualB + end + i32.const 42 + ) + (func $features/tail-calls/directVoid + ) + (func $features/tail-calls/comma (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + global.get $features/tail-calls/effects + return + end + global.get $features/tail-calls/effects + i32.const 1 + i32.add + global.set $features/tail-calls/effects + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/comma + ) + (func $features/tail-calls/bothCalls (param $0 i32) (result i32) + local.get $0 + i32.const 1 + i32.add + i32.const 1 + local.get $0 + select + ) + (func $~start + memory.size + i32.const 16 + i32.shl + i32.const 34640 + i32.sub + i32.const 1 + i32.shr_u + global.set $~lib/rt/itcms/threshold + i32.const 1236 + i32.const 1232 + i32.store + i32.const 1240 + i32.const 1232 + i32.store + i32.const 1232 + global.set $~lib/rt/itcms/pinSpace + i32.const 1268 + i32.const 1264 + i32.store + i32.const 1272 + i32.const 1264 + i32.store + i32.const 1264 + global.set $~lib/rt/itcms/toSpace + i32.const 1412 + i32.const 1408 + i32.store + i32.const 1416 + i32.const 1408 + i32.store + i32.const 1408 + global.set $~lib/rt/itcms/fromSpace + ) + (func $features/tail-calls/wrapped (param $0 i32) (result i32) + local.get $0 + i32.const 255 + i32.and + ) + (func $features/tail-calls/virtual (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 10 + call $~lib/rt/itcms/__new + local.tee $1 + i32.store + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $1 + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.const 9 + call $~lib/rt/itcms/__new + local.tee $1 + i32.store + end + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + global.get $~lib/memory/__stack_pointer + local.get $1 + call $~lib/object/Object#constructor + local.tee $1 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $0 + i32.const 2 + i32.add + local.get $0 + i32.const 1 + i32.add + local.get $1 + i32.const 8 + i32.sub + i32.load + i32.const 10 + i32.eq + select + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + return + end + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) + (func $features/tail-calls/switchCall (param $0 i32) (result i32) + block $case2|0 + block $case1|0 + local.get $0 + if + local.get $0 + i32.const 1 + i32.eq + br_if $case1|0 + br $case2|0 + end + local.get $0 + i32.const 1 + i32.add + return + end + local.get $0 + i32.const 2 + i32.add + return + end + local.get $0 + i32.const 3 + i32.add + ) + (func $features/tail-calls/stackPointer (result i32) + global.get $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/rawStep (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + local.get $0 + i32.const 1 + i32.sub + global.get $features/tail-calls/indirectIndex + return_call_indirect (type $0) + ) + (func $features/tail-calls/optionalIndirect (param $0 i32) (result i32) + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 0 + i32.const 1584 + i32.load + return_call_indirect (type $2) + ) + (func $features/tail-calls/optionalDirect (param $0 i32) (result i32) + local.get $0 + i32.const 7 + i32.add + ) + (func $features/tail-calls/optional@varargs (param $0 i32) (param $1 i32) (result i32) + block $1of1 + block $0of1 + block $outOfRange + global.get $~argumentsLength + i32.const 1 + i32.sub + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + i32.const 7 + local.set $1 + end + local.get $0 + local.get $1 + i32.add + ) + (func $features/tail-calls/nullableIndirect (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 1520 + i32.store + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1520 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/nullable (param $0 i32) (result i32) + local.get $0 + return_call $features/tail-calls/maybeBox + ) + (func $features/tail-calls/nonNull (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $0 + call $features/tail-calls/maybeBox + local.tee $0 + i32.store + local.get $0 + i32.eqz + if + i32.const 1616 + i32.const 1744 + i32.const 178 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + ) + (func $features/tail-calls/nestedArguments (param $0 i32) (result i32) + local.get $0 + i32.const 2 + i32.add + ) + (func $features/tail-calls/narrowed (result i32) + i32.const 255 + ) + (func $features/tail-calls/mutualA (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + local.get $0 + i32.const 1 + i32.sub + return_call $features/tail-calls/mutualB + ) + (func $features/tail-calls/managed (result i32) + (local $0 i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + block $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.const 8 + call $~lib/rt/itcms/__new + local.tee $1 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + global.get $~lib/memory/__stack_pointer + local.get $1 + call $~lib/object/Object#constructor + local.tee $1 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + i32.const 42 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + i32.store + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/rt/itcms/state + i32.const 0 + i32.gt_s + if + loop $while-continue|0 + global.get $~lib/rt/itcms/state + if + call $~lib/rt/itcms/step + drop + br $while-continue|0 + end + end + end + call $~lib/rt/itcms/step + drop + loop $while-continue|1 + global.get $~lib/rt/itcms/state + if + call $~lib/rt/itcms/step + drop + br $while-continue|1 + end + end + global.get $~lib/rt/itcms/total + i64.extend_i32_u + i64.const 200 + i64.mul + i64.const 100 + i64.div_u + i32.wrap_i64 + i32.const 1024 + i32.add + global.set $~lib/rt/itcms/threshold + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + i32.load + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + return + end + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + ) + (func $features/tail-calls/indirectStep (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 42 + return + end + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1 + i32.sub + global.get $features/tail-calls/indirectFunction + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/indirectOrder (result i32) + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 0 + global.set $features/tail-calls/order + i32.const 1 + global.set $features/tail-calls/order + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 12 + global.set $features/tail-calls/order + i32.const 1 + global.set $~argumentsLength + i32.const 40 + i32.const 1056 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/indirect (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1056 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/imported (param $0 i32) (result i32) + local.get $0 + return_call $features/tail-calls/host + ) + (func $features/tail-calls/getOrder (result i32) + global.get $features/tail-calls/order + ) + (func $features/tail-calls/discarded (param $0 i32) + ) + (func $features/tail-calls/deepRaw (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 1808 + i32.store + i32.const 1808 + i32.load + global.set $features/tail-calls/indirectIndex + local.get $0 + if (result i32) + local.get $0 + i32.const 1 + i32.sub + global.get $features/tail-calls/indirectIndex + call_indirect (type $0) + else + i32.const 42 + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/deepIndirect (param $0 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 1552 + global.set $features/tail-calls/indirectFunction + global.get $~lib/memory/__stack_pointer + i32.const 1552 + i32.store + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1552 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/convertedIndirect (param $0 i32) (result i64) + (local $1 i64) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 1056 + i32.store + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1056 + i32.load + call_indirect (type $0) + i64.extend_i32_s + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $features/tail-calls/converted (param $0 i32) (result i64) + local.get $0 + i32.const 1 + i32.add + i64.extend_i32_s + ) + (func $features/tail-calls/callArrow (param $0 i32) (result i32) + i32.const 1 + global.set $~argumentsLength + local.get $0 + i32.const 1088 + i32.load + return_call_indirect (type $0) + ) + (func $features/tail-calls/afterCall (param $0 i32) (result i32) + global.get $features/tail-calls/effects + i32.const 1 + i32.add + global.set $features/tail-calls/effects + local.get $0 + i32.const 1 + i32.add + ) + (func $export:features/tail-calls/temporaryIndirect (param $0 i32) (param $1 i32) (result i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1872 + i32.lt_s + if + i32.const 34672 + i32.const 34720 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + i32.const 1 + global.set $~argumentsLength + local.get $1 + local.get $0 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) +) diff --git a/tests/compiler/features/tail-calls.ts b/tests/compiler/features/tail-calls.ts new file mode 100644 index 0000000000..372ab23f05 --- /dev/null +++ b/tests/compiler/features/tail-calls.ts @@ -0,0 +1,221 @@ +function inc(x: i32): i32 { + return x + 1; +} + +function noop(): void {} + +export function direct(x: i32): i32 { + return inc(x); +} + +export function directVoid(): void { + return noop(); +} + +function invoke(fn: (x: i32) => i32, x: i32): i32 { + return fn(x); +} + +export function indirect(x: i32): i32 { + return invoke(inc, x); +} + +export function converted(x: i32): i64 { + return inc(x); +} + +function widenInvoke(fn: (x: i32) => i32, x: i32): i64 { + return fn(x); +} + +export function convertedIndirect(x: i32): i64 { + return widenInvoke(inc, x); +} + +class Box {} + +function maybeBox(x: i32): Box | null { + return x ? new Box() : null; +} + +function maybeInvoke(fn: (x: i32) => Box | null, x: i32): Box | null { + return fn(x); +} + +export function nullable(x: i32): Box | null { + return maybeBox(x); +} + +export function nullableIndirect(x: i32): Box | null { + return maybeInvoke(maybeBox, x); +} + +// These recursions also verify tail positions in both arms of control flow. +export function recursive(n: i32): i32 { + if (n == 0) return 42; + return recursive(n - 1); +} + +export function mutualA(n: i32): i32 { + if (n == 0) return 42; + return mutualB(n - 1); +} + +function mutualB(n: i32): i32 { + if (n == 0) return 42; + return mutualA(n - 1); +} + +export function ternary(n: i32): i32 { + return n == 0 ? 42 : ternary(n - 1); +} + +export function temporary(n: i32): i32 { + if (n == 0) return 42; + const result = temporary(n - 1); + return result; +} + +let effects: i32 = 0; + +export function comma(n: i32): i32 { + if (n == 0) return effects; + return (effects++, comma(n - 1)); +} + +export function switchCall(n: i32): i32 { + switch (n) { + case 0: return inc(n); + case 1: return inc(n + 1); + default: return inc(n + 2); + } +} + +export function bothCalls(n: i32): i32 { + return n ? inc(n) : inc(0); +} + +export function nestedArguments(n: i32): i32 { + return inc(inc(n)); +} + +export function branches(n: i32): i32 { + if (n) { + return inc(n); + } else { + return inc(0); + } +} + +export function recursiveIndirect(fn: (n: i32) => i32, n: i32): i32 { + return fn(n); +} + +let indirectFunction: (n: i32) => i32 = inc; + +function indirectStep(n: i32): i32 { + if (n == 0) return 42; + n -= 1; + return indirectFunction(n); +} + +export function deepIndirect(n: i32): i32 { + indirectFunction = indirectStep; + return recursiveIndirect(indirectStep, n); +} + +export function temporaryIndirect(fn: (n: i32) => i32, n: i32): i32 { + const result = fn(n); + return result; +} + +export function callTemporaryIndirect(n: i32): i32 { + return temporaryIndirect(inc, n); +} + +// Callee evaluation, argument evaluation, and the varargs stub must keep order. +let order: i32 = 0; +function choose(): (n: i32) => i32 { + order = order * 10 + 1; + return inc; +} +function argument(): i32 { + order = order * 10 + 2; + return 40; +} +export function indirectOrder(): i32 { + order = 0; + return choose()(argument()); +} +export function getOrder(): i32 { return order; } + +function optional(x: i32, y: i32 = 7): i32 { return x + y; } +export function optionalDirect(x: i32): i32 { return optional(x); } +export function optionalIndirect(x: i32): i32 { + const fn: (x: i32, y?: i32) => i32 = optional; + return fn(x); +} + +@inline +function inlined(x: i32): i32 { return inc(x); } +export function inlineCaller(x: i32): i32 { return inlined(x) + 1; } + +// Conversions, checks, and work following a call must not be bypassed. +function wide(): i32 { return 511; } +export function narrowed(): u8 { return wide(); } +function small(x: i32): u8 { return x; } +export function wrapped(x: i32): u8 { return small(x); } +export function discarded(x: i32): void { return inc(x); } +export function afterCall(x: i32): i32 { + const result = inc(x); + effects++; + return result; +} +export function nonTail(n: i32): i32 { + if (n == 0) return 0; + return nonTail(n - 1) + 1; +} +export function nonNull(x: i32): Box { return maybeBox(x)!; } + +export function voidCall(): void { return noop(); } +export function voidImplicit(): void { noop(); } + +class Value { + value: i32 = 42; +} +function readValue(value: Value): i32 { + __collect(); + return value.value; +} +export function managed(): i32 { + const value = new Value(); + return readValue(value); +} +export function stackPointer(): usize { return __stack_pointer; } + +// An expression-bodied arrow uses an implicit return. +const arrow: (x: i32) => i32 = (x: i32): i32 => inc(x); +export function callArrow(x: i32): i32 { return arrow(x); } + +class Base { + value(x: i32): i32 { return inc(x); } +} +class Derived extends Base { + value(x: i32): i32 { return inc(x + 1); } +} +function dispatch(value: Base, x: i32): i32 { return value.value(x); } +export function virtual(x: i32): i32 { return dispatch(new Derived(), x); } + +let indirectIndex: u32 = 0; +function rawStep(n: i32): i32 { + if (n == 0) return 42; + return call_indirect(indirectIndex, n - 1); +} +export function deepRaw(n: i32): i32 { + indirectIndex = rawStep.index; + return rawStep(n); +} + +@external("env", "tailHost") +declare function host(x: i32): i32; +export function imported(x: i32): i32 { return host(x); } diff --git a/tests/compiler/tail-calls-disabled.debug.wat b/tests/compiler/tail-calls-disabled.debug.wat new file mode 100644 index 0000000000..a292d16150 --- /dev/null +++ b/tests/compiler/tail-calls-disabled.debug.wat @@ -0,0 +1,107 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func)) + (type $2 (func (param i32 i32 i32 i32))) + (type $3 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~argumentsLength (mut i32) (i32.const 0)) + (memory $0 1) + (data $0 (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00,\00\00\00t\00a\00i\00l\00-\00c\00a\00l\00l\00s\00-\00d\00i\00s\00a\00b\00l\00e\00d\00.\00t\00s\00") + (data $1 (i32.const 76) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $tail-calls-disabled/inc) + (export "direct" (func $tail-calls-disabled/direct)) + (export "indirect" (func $tail-calls-disabled/indirect)) + (export "ternary" (func $tail-calls-disabled/ternary)) + (export "memory" (memory $0)) + (start $~start) + (func $tail-calls-disabled/inc (param $x i32) (result i32) + local.get $x + i32.const 1 + i32.add + return + ) + (func $tail-calls-disabled/direct (param $x i32) (result i32) + local.get $x + call $tail-calls-disabled/inc + return + ) + (func $tail-calls-disabled/indirect (param $fn i32) (param $x i32) (result i32) + local.get $x + i32.const 1 + global.set $~argumentsLength + local.get $fn + i32.load + call_indirect (type $0) + return + ) + (func $tail-calls-disabled/ternary (param $x i32) (result i32) + local.get $x + if (result i32) + local.get $x + call $tail-calls-disabled/inc + else + i32.const 0 + call $tail-calls-disabled/inc + end + return + ) + (func $start:tail-calls-disabled + i32.const 40 + call $tail-calls-disabled/direct + i32.const 41 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 9 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 96 + i32.const 40 + call $tail-calls-disabled/indirect + i32.const 41 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 10 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + call $tail-calls-disabled/ternary + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 11 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 40 + call $tail-calls-disabled/ternary + i32.const 41 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 12 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $~start + call $start:tail-calls-disabled + ) +) diff --git a/tests/compiler/tail-calls-disabled.json b/tests/compiler/tail-calls-disabled.json new file mode 100644 index 0000000000..27012d7b18 --- /dev/null +++ b/tests/compiler/tail-calls-disabled.json @@ -0,0 +1,3 @@ +{ + "asc_flags": ["--runtime stub"] +} diff --git a/tests/compiler/tail-calls-disabled.release.wat b/tests/compiler/tail-calls-disabled.release.wat new file mode 100644 index 0000000000..45b2d91877 --- /dev/null +++ b/tests/compiler/tail-calls-disabled.release.wat @@ -0,0 +1,54 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func)) + (type $2 (func (param i32 i32 i32 i32))) + (type $3 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data $0 (i32.const 1036) "<") + (data $0.1 (i32.const 1048) "\02\00\00\00,\00\00\00t\00a\00i\00l\00-\00c\00a\00l\00l\00s\00-\00d\00i\00s\00a\00b\00l\00e\00d\00.\00t\00s") + (data $1 (i32.const 1100) "\1c") + (data $1.1 (i32.const 1112) "\04\00\00\00\08\00\00\00\01") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $tail-calls-disabled/inc) + (export "direct" (func $tail-calls-disabled/inc)) + (export "indirect" (func $tail-calls-disabled/indirect)) + (export "ternary" (func $tail-calls-disabled/ternary)) + (export "memory" (memory $0)) + (start $~start) + (func $~start + i32.const 40 + i32.const 1120 + i32.load + call_indirect (type $0) + i32.const 41 + i32.ne + if + i32.const 0 + i32.const 1056 + i32.const 10 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $tail-calls-disabled/ternary (param $0 i32) (result i32) + local.get $0 + i32.const 1 + i32.add + i32.const 1 + local.get $0 + select + ) + (func $tail-calls-disabled/indirect (param $0 i32) (param $1 i32) (result i32) + local.get $1 + local.get $0 + i32.load + call_indirect (type $0) + ) + (func $tail-calls-disabled/inc (param $0 i32) (result i32) + local.get $0 + i32.const 1 + i32.add + ) +) diff --git a/tests/compiler/tail-calls-disabled.ts b/tests/compiler/tail-calls-disabled.ts new file mode 100644 index 0000000000..d68e5cd350 --- /dev/null +++ b/tests/compiler/tail-calls-disabled.ts @@ -0,0 +1,12 @@ +function inc(x: i32): i32 { return x + 1; } + +export function direct(x: i32): i32 { return inc(x); } + +export function indirect(fn: (x: i32) => i32, x: i32): i32 { return fn(x); } + +export function ternary(x: i32): i32 { return x ? inc(x) : inc(0); } + +assert(direct(40) == 41); +assert(indirect(inc, 40) == 41); +assert(ternary(0) == 1); +assert(ternary(40) == 41); diff --git a/tests/features.json b/tests/features.json index 5c47772500..f92c07759b 100644 --- a/tests/features.json +++ b/tests/features.json @@ -29,6 +29,13 @@ "v8_flags": [ ] }, + "tail-calls": { + "asc_flags": [ + "--enable tail-calls" + ], + "v8_flags": [ + ] + }, "simd": { "asc_flags": [ "--enable simd"