diff options
10 files changed, 100 insertions, 219 deletions
diff --git a/src/pass.h b/src/pass.h index 02cea7879..60d6692a1 100644 --- a/src/pass.h +++ b/src/pass.h @@ -259,6 +259,8 @@ struct PassRunner { PassRunner(const PassRunner&) = delete; PassRunner& operator=(const PassRunner&) = delete; + virtual ~PassRunner() = default; + // But we can make it easy to create a nested runner // TODO: Go through and use this in more places explicit PassRunner(const PassRunner* runner) @@ -341,6 +343,9 @@ struct PassRunner { // Returns whether a pass by that name will remove debug info. static bool passRemovesDebugInfo(const std::string& name); +protected: + virtual void doAdd(std::unique_ptr<Pass> pass); + private: // Whether this is a nested pass runner. bool isNested = false; @@ -352,8 +357,6 @@ private: // Whether this pass runner has run. A pass runner should only be run once. bool ran = false; - void doAdd(std::unique_ptr<Pass> pass); - void runPass(Pass* pass); void runPassOnFunction(Pass* pass, Function* func); diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 342cd013d..365891db4 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -848,6 +848,54 @@ public: } }; +// Proxy that runs wrapped pass for instrumented functions only +struct InstrumentedProxy : public Pass { + std::unique_ptr<Pass> create() override { + return std::make_unique<InstrumentedProxy>(analyzer, pass->create()); + } + + InstrumentedProxy(ModuleAnalyzer* analyzer, std::unique_ptr<Pass> pass) + : analyzer(analyzer), pass(std::move(pass)) {} + + bool isFunctionParallel() override { return pass->isFunctionParallel(); } + + void runOnFunction(Module* module, Function* func) override { + if (!analyzer->needsInstrumentation(func)) { + return; + } + if (pass->getPassRunner() == nullptr) { + pass->setPassRunner(getPassRunner()); + } + pass->runOnFunction(module, func); + } + + bool modifiesBinaryenIR() override { return pass->modifiesBinaryenIR(); } + + bool invalidatesDWARF() override { return pass->invalidatesDWARF(); } + + bool requiresNonNullableLocalFixups() override { + return pass->requiresNonNullableLocalFixups(); + } + +private: + ModuleAnalyzer* analyzer; + std::unique_ptr<Pass> pass; +}; + +struct InstrumentedPassRunner : public PassRunner { + InstrumentedPassRunner(Module* wasm, ModuleAnalyzer* analyzer) + : PassRunner(wasm), analyzer(analyzer) {} + +protected: + void doAdd(std::unique_ptr<Pass> pass) override { + PassRunner::doAdd( + std::unique_ptr<Pass>(new InstrumentedProxy(analyzer, std::move(pass)))); + } + +private: + ModuleAnalyzer* analyzer; +}; + // Instrument control flow, around calls and adding skips for rewinding. struct AsyncifyFlow : public Pass { bool isFunctionParallel() override { return true; } @@ -1616,7 +1664,7 @@ struct Asyncify : public Pass { // practical to add code around each call, without affecting // anything else. { - PassRunner runner(module); + InstrumentedPassRunner runner(module, &analyzer); runner.add("flatten"); // Dce is useful here, since AsyncifyFlow makes control flow conditional, // which may make unreachable code look reachable. It also lets us ignore @@ -1647,7 +1695,7 @@ struct Asyncify : public Pass { // restore those locals). We also and optimize after as well to simplify // the code as much as possible. { - PassRunner runner(module); + InstrumentedPassRunner runner(module, &analyzer); if (optimize) { runner.addDefaultFunctionOptimizationPasses(); } diff --git a/test/lit/passes/asyncify_enable-multivalue.wast b/test/lit/passes/asyncify_enable-multivalue.wast index 28210034b..c9a84d458 100644 --- a/test/lit/passes/asyncify_enable-multivalue.wast +++ b/test/lit/passes/asyncify_enable-multivalue.wast @@ -37,18 +37,10 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) ;; CHECK: (func $do_sleep - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (global.get $sleeping) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (global.get $sleeping) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: (block ;; CHECK-NEXT: (global.set $sleeping ;; CHECK-NEXT: (i32.const 1) @@ -834,15 +826,11 @@ (drop (call $import2)) ) ;; CHECK: (func $calls-nothing - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.const 17) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-nothing (drop (i32.eqz (i32.const 17))) diff --git a/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind.wast b/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind.wast index dc89dab7f..ea14c2400 100644 --- a/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind.wast +++ b/test/lit/passes/asyncify_mod-asyncify-always-and-only-unwind.wast @@ -415,15 +415,11 @@ (drop (call $import2)) ) ;; CHECK: (func $calls-nothing - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.const 17) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-nothing (drop (i32.eqz (i32.const 17))) diff --git a/test/lit/passes/asyncify_mod-asyncify-never-unwind.wast b/test/lit/passes/asyncify_mod-asyncify-never-unwind.wast index dce366b82..67bfb5694 100644 --- a/test/lit/passes/asyncify_mod-asyncify-never-unwind.wast +++ b/test/lit/passes/asyncify_mod-asyncify-never-unwind.wast @@ -433,15 +433,11 @@ (drop (call $import2)) ) ;; CHECK: (func $calls-nothing - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.const 17) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-nothing (drop (i32.eqz (i32.const 17))) diff --git a/test/lit/passes/asyncify_optimize-level=1.wast b/test/lit/passes/asyncify_optimize-level=1.wast index 0affdb757..65432a2ea 100644 --- a/test/lit/passes/asyncify_optimize-level=1.wast +++ b/test/lit/passes/asyncify_optimize-level=1.wast @@ -301,7 +301,11 @@ (drop (call $import2)) ) ;; CHECK: (func $calls-nothing - ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.const 17) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-nothing (drop (i32.eqz (i32.const 17))) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast index 03a8dfe96..cd42c9c2d 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast @@ -39,50 +39,14 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) ;; CHECK: (func $calls-import - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $import) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.ne - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) (func $calls-import (call $import) ) ;; CHECK: (func $calls-import2-drop - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (call $import2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.ne - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $import2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import2-drop @@ -90,49 +54,10 @@ ) ;; CHECK: (func $returns (result i32) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) - ;; CHECK-NEXT: (local $4 i32) - ;; CHECK-NEXT: (local $5 i32) - ;; CHECK-NEXT: (local $6 i32) - ;; CHECK-NEXT: (local.set $5 - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (local.set $6 - ;; CHECK-NEXT: (call $import2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.ne - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (local.get $5) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $6) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $3 - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $4 - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (local.get $4) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (call $import2) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) (func $returns (result i32) (local $x i32) @@ -140,27 +65,8 @@ (local.get $x) ) ;; CHECK: (func $calls-indirect (param $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call_indirect (type $f) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.ne - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_indirect (type $f) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-indirect (param $x i32) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-ignore-imports.wast b/test/lit/passes/asyncify_pass-arg=asyncify-ignore-imports.wast index 9fd019f8c..7cd0137f3 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-ignore-imports.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-ignore-imports.wast @@ -45,24 +45,16 @@ (call $import) ) ;; CHECK: (func $calls-import2-drop - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (call $import2) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (call $import2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import2-drop (drop (call $import2)) ) ;; CHECK: (func $calls-import2-if-else (param $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (call $import3 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-ignore-indirect.wast b/test/lit/passes/asyncify_pass-arg=asyncify-ignore-indirect.wast index 1d7fd1797..83de99da1 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-ignore-indirect.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-ignore-indirect.wast @@ -485,12 +485,8 @@ ) ) ;; CHECK: (func $calls-indirect (param $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect (type $f) - ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-indirect (param $x i32) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-imports@env.import,env.import2.wast b/test/lit/passes/asyncify_pass-arg=asyncify-imports@env.import,env.import2.wast index 5b355ba06..89690a3f0 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-imports@env.import,env.import2.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-imports@env.import,env.import2.wast @@ -36,18 +36,10 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) ;; CHECK: (func $do_sleep - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (global.get $sleeping) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (global.get $sleeping) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: (block ;; CHECK-NEXT: (global.set $sleeping ;; CHECK-NEXT: (i32.const 1) @@ -826,15 +818,11 @@ (drop (call $import2)) ) ;; CHECK: (func $calls-nothing - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.const 17) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-nothing (drop (i32.eqz (i32.const 17))) @@ -1163,12 +1151,8 @@ ) ) ;; CHECK: (func $calls-import2-if-else (param $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (call $import3 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -1184,28 +1168,18 @@ ) ) ;; CHECK: (func $calls-import2-if-else-oneside (param $x i32) (result i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import2-if-else-oneside (param $x i32) (result i32) (if (local.get $x) @@ -1215,28 +1189,18 @@ (return (i32.const 3)) ) ;; CHECK: (func $calls-import2-if-else-oneside2 (param $x i32) (result i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (call $import3 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (call $import3 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import2-if-else-oneside2 (param $x i32) (result i32) (if (local.get $x) @@ -1246,30 +1210,18 @@ (return (i32.const 3)) ) ;; CHECK: (func $calls-loop (param $x i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (loop $l ;; CHECK-NEXT: (call $import3 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $3 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br_if $l - ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) |