From 164e1ae7579373a63054873005ec6631678af500 Mon Sep 17 00:00:00 2001 From: dcode Date: Thu, 20 Jun 2019 19:47:12 +0200 Subject: [PATCH 1/2] Simplify static instanceof if there are no side-effects --- src/compiler.ts | 15 +- src/module.ts | 78 +++---- tests/compiler/instanceof.untouched.wat | 10 - tests/compiler/rt/instanceof.untouched.wat | 6 - tests/compiler/std/arraybuffer.untouched.wat | 217 ++----------------- 5 files changed, 62 insertions(+), 264 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 1f6c0f851d..ef49b2330d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -42,7 +42,8 @@ import { isLocalTee, getLocalSetIndex, FeatureFlags, - needsExplicitUnreachable + needsExplicitUnreachable, + hasSideEffects } from "./module"; import { @@ -7355,10 +7356,14 @@ export class Compiler extends DiagnosticEmitter { // downcast - check statically if (actualType.isAssignableTo(expectedType)) { - return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, false, false, expression.expression), - module.i32(1) - ], NativeType.I32); + if (hasSideEffects(expr)) { + return module.block(null, [ + this.convertExpression(expr, actualType, Type.void, false, false, expression.expression), + module.i32(1) + ], NativeType.I32); + } else { + return module.i32(1); + } // upcast - check dynamically } else if (expectedType.isAssignableTo(actualType)) { diff --git a/src/module.ts b/src/module.ts index d8396671d9..223aa8e524 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1194,7 +1194,7 @@ export class Module { var nested1: ExpressionRef, nested2: ExpressionRef; - switch (_BinaryenExpressionGetId(expr)) { + switch (_BinaryenExpressionGetId(expr)) { case ExpressionId.Const: { switch (_BinaryenExpressionGetType(expr)) { case NativeType.I32: { @@ -1561,44 +1561,44 @@ export class Relooper { } } -// export function hasSideEffects(expr: ExpressionRef): bool { -// switch (_BinaryenExpressionGetId(expr = getPtr(expr))) { -// case ExpressionId.GetLocal: -// case ExpressionId.GetGlobal: -// case ExpressionId.Const: -// case ExpressionId.Nop: -// case ExpressionId.Unreachable: { -// return false; -// } -// case ExpressionId.Block: { -// for (let i = 0, k = _BinaryenBlockGetNumChildren(expr); i < k; ++i) { -// if (hasSideEffects(_BinaryenBlockGetChild(expr, i))) return true; -// } -// return false; -// } -// case ExpressionId.If: { -// return hasSideEffects(_BinaryenIfGetCondition(expr)) -// || hasSideEffects(_BinaryenIfGetIfTrue(expr)) -// || hasSideEffects(_BinaryenIfGetIfFalse(expr)); -// } -// case ExpressionId.Unary: { -// return hasSideEffects(_BinaryenUnaryGetValue(expr)); -// } -// case ExpressionId.Binary: { -// return hasSideEffects(_BinaryenBinaryGetLeft(expr)) -// || hasSideEffects(_BinaryenBinaryGetRight(expr)); -// } -// case ExpressionId.Drop: { -// return hasSideEffects(_BinaryenDropGetValue(expr)); -// } -// case ExpressionId.Select: { -// return hasSideEffects(_BinaryenSelectGetIfTrue(expr)) -// || hasSideEffects(_BinaryenSelectGetIfFalse(expr)) -// || hasSideEffects(_BinaryenSelectGetCondition(expr)); -// } -// } -// return true; -// } +export function hasSideEffects(expr: ExpressionRef): bool { + // TODO: there's more + switch (_BinaryenExpressionGetId(expr)) { + case ExpressionId.LocalGet: + case ExpressionId.GlobalGet: + case ExpressionId.Const: + case ExpressionId.Nop: { + return false; + } + case ExpressionId.Block: { + for (let i = 0, k = _BinaryenBlockGetNumChildren(expr); i < k; ++i) { + if (hasSideEffects(_BinaryenBlockGetChild(expr, i))) return true; + } + return false; + } + case ExpressionId.If: { + return hasSideEffects(_BinaryenIfGetCondition(expr)) + || hasSideEffects(_BinaryenIfGetIfTrue(expr)) + || hasSideEffects(_BinaryenIfGetIfFalse(expr)); + } + case ExpressionId.Unary: { + return hasSideEffects(_BinaryenUnaryGetValue(expr)); + } + case ExpressionId.Binary: { + return hasSideEffects(_BinaryenBinaryGetLeft(expr)) + || hasSideEffects(_BinaryenBinaryGetRight(expr)); + } + case ExpressionId.Drop: { + return hasSideEffects(_BinaryenDropGetValue(expr)); + } + case ExpressionId.Select: { + return hasSideEffects(_BinaryenSelectGetIfTrue(expr)) + || hasSideEffects(_BinaryenSelectGetIfFalse(expr)) + || hasSideEffects(_BinaryenSelectGetCondition(expr)); + } + } + return true; +} // helpers // can't do stack allocation here: STACKTOP is a global in WASM but a hidden variable in asm.js diff --git a/tests/compiler/instanceof.untouched.wat b/tests/compiler/instanceof.untouched.wat index 7136f6e13a..48fa5952ca 100644 --- a/tests/compiler/instanceof.untouched.wat +++ b/tests/compiler/instanceof.untouched.wat @@ -79,8 +79,6 @@ (func $start:instanceof (; 7 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) - global.get $instanceof/a - drop i32.const 1 i32.eqz if @@ -91,8 +89,6 @@ call $~lib/builtins/abort unreachable end - global.get $instanceof/b - drop i32.const 1 i32.eqz if @@ -155,8 +151,6 @@ call $~lib/builtins/abort unreachable end - global.get $instanceof/b - drop i32.const 1 i32.eqz if @@ -587,8 +581,6 @@ call $~lib/builtins/abort unreachable end - global.get $instanceof/an - drop i32.const 1 i32.eqz if @@ -625,8 +617,6 @@ call $~lib/builtins/abort unreachable end - global.get $instanceof/an - drop i32.const 1 i32.eqz if diff --git a/tests/compiler/rt/instanceof.untouched.wat b/tests/compiler/rt/instanceof.untouched.wat index 7816af7b8b..31ccc85f7e 100644 --- a/tests/compiler/rt/instanceof.untouched.wat +++ b/tests/compiler/rt/instanceof.untouched.wat @@ -220,8 +220,6 @@ i32.const 0 call $rt/instanceof/BlackCat#constructor global.set $rt/instanceof/blackcat - global.get $rt/instanceof/animal - drop i32.const 1 i32.eqz if @@ -272,8 +270,6 @@ call $~lib/builtins/abort unreachable end - global.get $rt/instanceof/cat - drop i32.const 1 i32.eqz if @@ -323,8 +319,6 @@ call $~lib/builtins/abort unreachable end - global.get $rt/instanceof/blackcat - drop i32.const 1 i32.eqz if diff --git a/tests/compiler/std/arraybuffer.untouched.wat b/tests/compiler/std/arraybuffer.untouched.wat index efd6512728..577fbb588f 100644 --- a/tests/compiler/std/arraybuffer.untouched.wat +++ b/tests/compiler/std/arraybuffer.untouched.wat @@ -3567,127 +3567,12 @@ local.get $1 return end - local.get $0 - drop i32.const 1 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + local.set $1 local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + call $~lib/rt/pure/__release + local.get $1 + return end i32.const 0 local.set $1 @@ -3757,83 +3642,12 @@ local.get $1 return end - local.get $0 - drop i32.const 1 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + local.set $1 local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + call $~lib/rt/pure/__release + local.get $1 + return end i32.const 0 local.set $1 @@ -3969,17 +3783,12 @@ local.get $1 return end - local.get $0 - drop i32.const 1 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + local.set $1 + local.get $0 + call $~lib/rt/pure/__release + local.get $1 + return end i32.const 0 local.set $1 From 845cabb5baea5d1d74d5f3b7b3249a8aab008298 Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 21 Jun 2019 15:57:04 +0200 Subject: [PATCH 2/2] Utilize vacuum in precomputation as a more general solution --- src/compiler.ts | 15 +- src/module.ts | 86 ++-- tests/compiler/instanceof.untouched.wat | 54 +-- tests/compiler/rt/instanceof.untouched.wat | 6 + tests/compiler/std/arraybuffer.untouched.wat | 405 +------------------ 5 files changed, 73 insertions(+), 493 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index ef49b2330d..1f6c0f851d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -42,8 +42,7 @@ import { isLocalTee, getLocalSetIndex, FeatureFlags, - needsExplicitUnreachable, - hasSideEffects + needsExplicitUnreachable } from "./module"; import { @@ -7356,14 +7355,10 @@ export class Compiler extends DiagnosticEmitter { // downcast - check statically if (actualType.isAssignableTo(expectedType)) { - if (hasSideEffects(expr)) { - return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, false, false, expression.expression), - module.i32(1) - ], NativeType.I32); - } else { - return module.i32(1); - } + return module.block(null, [ + this.convertExpression(expr, actualType, Type.void, false, false, expression.expression), + module.i32(1) + ], NativeType.I32); // upcast - check dynamically } else if (expectedType.isAssignableTo(actualType)) { diff --git a/src/module.ts b/src/module.ts index 223aa8e524..9dad851750 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1106,10 +1106,16 @@ export class Module { var func = this.addTemporaryFunction(type, null, expr); var names = this.cachedPrecomputeNames; if (!names) { - this.cachedPrecomputeNames = names = allocI32Array([ this.allocStringCached("precompute") ]); + this.cachedPrecomputeNames = names = allocI32Array([ + this.allocStringCached("vacuum"), + this.allocStringCached("precompute") + ]); } - _BinaryenFunctionRunPasses(func, this.ref, names, 1); + _BinaryenFunctionRunPasses(func, this.ref, names, 2); expr = _BinaryenFunctionGetBody(func); + if (_BinaryenExpressionGetId(expr) == ExpressionId.Return) { + expr = _BinaryenReturnGetValue(expr); + } this.removeTemporaryFunction(); // reset optimize levels to previous @@ -1561,44 +1567,44 @@ export class Relooper { } } -export function hasSideEffects(expr: ExpressionRef): bool { - // TODO: there's more - switch (_BinaryenExpressionGetId(expr)) { - case ExpressionId.LocalGet: - case ExpressionId.GlobalGet: - case ExpressionId.Const: - case ExpressionId.Nop: { - return false; - } - case ExpressionId.Block: { - for (let i = 0, k = _BinaryenBlockGetNumChildren(expr); i < k; ++i) { - if (hasSideEffects(_BinaryenBlockGetChild(expr, i))) return true; - } - return false; - } - case ExpressionId.If: { - return hasSideEffects(_BinaryenIfGetCondition(expr)) - || hasSideEffects(_BinaryenIfGetIfTrue(expr)) - || hasSideEffects(_BinaryenIfGetIfFalse(expr)); - } - case ExpressionId.Unary: { - return hasSideEffects(_BinaryenUnaryGetValue(expr)); - } - case ExpressionId.Binary: { - return hasSideEffects(_BinaryenBinaryGetLeft(expr)) - || hasSideEffects(_BinaryenBinaryGetRight(expr)); - } - case ExpressionId.Drop: { - return hasSideEffects(_BinaryenDropGetValue(expr)); - } - case ExpressionId.Select: { - return hasSideEffects(_BinaryenSelectGetIfTrue(expr)) - || hasSideEffects(_BinaryenSelectGetIfFalse(expr)) - || hasSideEffects(_BinaryenSelectGetCondition(expr)); - } - } - return true; -} +// export function hasSideEffects(expr: ExpressionRef): bool { +// // TODO: there's more +// switch (_BinaryenExpressionGetId(expr)) { +// case ExpressionId.LocalGet: +// case ExpressionId.GlobalGet: +// case ExpressionId.Const: +// case ExpressionId.Nop: { +// return false; +// } +// case ExpressionId.Block: { +// for (let i = 0, k = _BinaryenBlockGetNumChildren(expr); i < k; ++i) { +// if (hasSideEffects(_BinaryenBlockGetChild(expr, i))) return true; +// } +// return false; +// } +// case ExpressionId.If: { +// return hasSideEffects(_BinaryenIfGetCondition(expr)) +// || hasSideEffects(_BinaryenIfGetIfTrue(expr)) +// || hasSideEffects(_BinaryenIfGetIfFalse(expr)); +// } +// case ExpressionId.Unary: { +// return hasSideEffects(_BinaryenUnaryGetValue(expr)); +// } +// case ExpressionId.Binary: { +// return hasSideEffects(_BinaryenBinaryGetLeft(expr)) +// || hasSideEffects(_BinaryenBinaryGetRight(expr)); +// } +// case ExpressionId.Drop: { +// return hasSideEffects(_BinaryenDropGetValue(expr)); +// } +// case ExpressionId.Select: { +// return hasSideEffects(_BinaryenSelectGetIfTrue(expr)) +// || hasSideEffects(_BinaryenSelectGetIfFalse(expr)) +// || hasSideEffects(_BinaryenSelectGetCondition(expr)); +// } +// } +// return true; +// } // helpers // can't do stack allocation here: STACKTOP is a global in WASM but a hidden variable in asm.js diff --git a/tests/compiler/instanceof.untouched.wat b/tests/compiler/instanceof.untouched.wat index 48fa5952ca..31257b0dd9 100644 --- a/tests/compiler/instanceof.untouched.wat +++ b/tests/compiler/instanceof.untouched.wat @@ -19,56 +19,20 @@ (export "memory" (memory $0)) (start $start) (func $instanceof/isI32 (; 1 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - local.get $0 - drop i32.const 1 - if - i32.const 1 - return - else - i32.const 0 - return - end - unreachable + return ) (func $instanceof/isI32 (; 2 ;) (type $FUNCSIG$id) (param $0 f64) (result i32) - local.get $0 - drop i32.const 0 - if - i32.const 1 - return - else - i32.const 0 - return - end - unreachable + return ) (func $instanceof/isI32 (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - local.get $0 - drop i32.const 0 - if - i32.const 1 - return - else - i32.const 0 - return - end - unreachable + return ) (func $instanceof/isI32 (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - local.get $0 - drop i32.const 0 - if - i32.const 1 - return - else - i32.const 0 - return - end - unreachable + return ) (func $~lib/rt/stub/__retain (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 @@ -79,6 +43,8 @@ (func $start:instanceof (; 7 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) + global.get $instanceof/a + drop i32.const 1 i32.eqz if @@ -89,6 +55,8 @@ call $~lib/builtins/abort unreachable end + global.get $instanceof/b + drop i32.const 1 i32.eqz if @@ -151,6 +119,8 @@ call $~lib/builtins/abort unreachable end + global.get $instanceof/b + drop i32.const 1 i32.eqz if @@ -581,6 +551,8 @@ call $~lib/builtins/abort unreachable end + global.get $instanceof/an + drop i32.const 1 i32.eqz if @@ -617,6 +589,8 @@ call $~lib/builtins/abort unreachable end + global.get $instanceof/an + drop i32.const 1 i32.eqz if diff --git a/tests/compiler/rt/instanceof.untouched.wat b/tests/compiler/rt/instanceof.untouched.wat index 31ccc85f7e..7816af7b8b 100644 --- a/tests/compiler/rt/instanceof.untouched.wat +++ b/tests/compiler/rt/instanceof.untouched.wat @@ -220,6 +220,8 @@ i32.const 0 call $rt/instanceof/BlackCat#constructor global.set $rt/instanceof/blackcat + global.get $rt/instanceof/animal + drop i32.const 1 i32.eqz if @@ -270,6 +272,8 @@ call $~lib/builtins/abort unreachable end + global.get $rt/instanceof/cat + drop i32.const 1 i32.eqz if @@ -319,6 +323,8 @@ call $~lib/builtins/abort unreachable end + global.get $rt/instanceof/blackcat + drop i32.const 1 i32.eqz if diff --git a/tests/compiler/std/arraybuffer.untouched.wat b/tests/compiler/std/arraybuffer.untouched.wat index 577fbb588f..17c7ea52f2 100644 --- a/tests/compiler/std/arraybuffer.untouched.wat +++ b/tests/compiler/std/arraybuffer.untouched.wat @@ -3320,138 +3320,7 @@ drop local.get $0 if - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end + nop end i32.const 0 local.set $1 @@ -3462,90 +3331,7 @@ (func $~lib/arraybuffer/ArrayBuffer.isView (; 31 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 if - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - return - end + nop end i32.const 0 ) @@ -3556,17 +3342,6 @@ drop local.get $0 if - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end i32.const 1 local.set $1 local.get $0 @@ -3587,61 +3362,6 @@ drop local.get $0 if - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end i32.const 1 local.set $1 local.get $0 @@ -3662,127 +3382,6 @@ drop local.get $0 if - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end - local.get $0 - drop - i32.const 0 - if - i32.const 1 - local.set $1 - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - return - end i32.const 1 local.set $1 local.get $0