From 2098245bf896704da6304874b6204aaca322cb15 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 20 Nov 2025 10:55:16 -0800 Subject: [PATCH 01/44] start --- src/passes/DeadArgumentElimination.cpp | 57 +++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 42d8d87a6a9..0fb8a2f4971 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -60,6 +60,8 @@ struct DAEFunctionInfo { // Whether this needs to be recomputed. This begins as true for the first // computation, and we reset it every time we touch the function. bool stale = true; + // TODO + bool justUpdated = false; // The unused parameters, if any. SortedVector unusedParams; // Maps a function name to the calls going to it. @@ -160,18 +162,21 @@ struct DAEScanner if (!info->stale) { // Nothing changed since last time. + info->justUpdated = false; return; } // Clear the data, mark us as no longer stale, and recompute everything. info->clear(); info->stale = false; + info->justUpdated = true; - auto numParams = func->getNumParams(); PostWalker>::doWalkFunction(func); + // If there are params, check if they are used. // TODO: This work could be avoided if we cannot optimize for other reasons. // That would require deferring this to later and checking that. + auto numParams = func->getNumParams(); if (numParams > 0) { auto usedParams = ParamUtils::getUsedParams(func, getModule()); for (Index i = 0; i < numParams; i++) { @@ -195,6 +200,9 @@ struct DAE : public Pass { // Map of function names to indexes. This lets us use indexes below for speed. std::unordered_map indexes; + // TODO comment + std::vector> allCalls; + void run(Module* module) override { DAEFunctionInfoMap infoMap; // Ensure all entries exist so the parallel threads don't modify the data @@ -211,6 +219,8 @@ struct DAE : public Pass { indexes[module->functions[i]->name] = i; } + allCalls.resize(numFunctions); + // Iterate to convergence. while (1) { if (!iteration(module, infoMap)) { @@ -232,6 +242,8 @@ struct DAE : public Pass { // of computing this map is significant, so we compute it once at the start // and then use that possibly-over-approximating data. std::vector> callers; + // TODO + std::vector> callees; bool iteration(Module* module, DAEFunctionInfoMap& infoMap) { allDroppedCalls.clear(); @@ -257,15 +269,10 @@ struct DAE : public Pass { scanner.run(getPassRunner(), module); // Combine all the info from the scan. - std::vector> allCalls(numFunctions); std::vector tailCallees(numFunctions); std::vector hasUnseenCalls(numFunctions); for (auto& [func, info] : infoMap) { - for (auto& [name, calls] : info.calls) { - auto& allCallsToName = allCalls[indexes[name]]; - allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); - } for (auto& callee : info.tailCallees) { tailCallees[indexes[callee]] = true; } @@ -294,9 +301,47 @@ struct DAE : public Pass { } // Copy into efficient vectors. callers.resize(numFunctions); + callees.resize(numFunctions); for (Index i = 0; i < numFunctions; ++i) { auto& set = callersSets[i]; callers[i] = std::vector(set.begin(), set.end()); + for (auto& caller : callers[i]) { + callees[indexes[caller]].push_back(i); + } + } + } + + // Recompute parts of allCalls as necessary. We know which function infos + // were just updated, and start there: If we updated { A, B }, and A calls + // C while B calls nothing, then the list of all calls must be updated for + // D. If D is called by not only A but also some other (not just updated) + // function X, that means we must scan { A, X }. First, find the things + // called by just-updated functions. + std::unordered_set calledByJustUpdated; + for (auto& [func, info] : infoMap) { + if (info.justUpdated) { + for (auto& callee : callees[indexes[func]]) { + calledByJustUpdated.insert(callee); + } + } + } + // Find all their callers, so we can process the calls from them, thus + // finding all the calls to |calledByJustUpdated|. + std::unordered_set relevantCallers; + for (auto& called : calledByJustUpdated) { + auto calledIndex = indexes[called]; + for (auto& caller : callers[calledIndex]) { + relevantCallers.insert(caller); + } + // Clear the old call data before we fill it below. + allCalls[calledIndex].clear(); + } + // Process those callers. + for (auto& caller : relevantCallers) { + auto& info = infoMap[caller]; + for (auto& [name, calls] : info.calls) { + auto& allCallsToName = allCalls[indexes[name]]; + allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); } } From 468be273e2a833a776068a8d599fa62094cddbec Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 20 Nov 2025 11:32:49 -0800 Subject: [PATCH 02/44] work --- src/passes/DeadArgumentElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 0fb8a2f4971..69aa03675f1 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -321,7 +321,7 @@ struct DAE : public Pass { for (auto& [func, info] : infoMap) { if (info.justUpdated) { for (auto& callee : callees[indexes[func]]) { - calledByJustUpdated.insert(callee); + calledByJustUpdated.insert(module->functions[callee]->name); } } } From c43bd44c7308ce04f1850d3f79d23b17bd19836e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 20 Nov 2025 13:33:00 -0800 Subject: [PATCH 03/44] work --- src/passes/DeadArgumentElimination.cpp | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 69aa03675f1..20da87c62eb 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -311,6 +311,8 @@ struct DAE : public Pass { } } +std::cout << "iter\n" << *module << '\n'; + // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls // C while B calls nothing, then the list of all calls must be updated for @@ -320,6 +322,7 @@ struct DAE : public Pass { std::unordered_set calledByJustUpdated; for (auto& [func, info] : infoMap) { if (info.justUpdated) { +std::cout << "just updated " << func << '\n'; for (auto& callee : callees[indexes[func]]) { calledByJustUpdated.insert(module->functions[callee]->name); } @@ -327,6 +330,53 @@ struct DAE : public Pass { } // Find all their callers, so we can process the calls from them, thus // finding all the calls to |calledByJustUpdated|. + // XXX this is bad... we need to close over repeated operations here, see + // $1 here: + /* + (module + (rec + (type $S (struct)) + (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) + ) + + (func $0 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (unreachable) + ) + (func $1 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (unreachable) + ) + (func $2 + (drop + (call $1 + (struct.new $0 + (call $6) + (ref.func $0) + ) + (struct.new_default $S) + ) + ) + ) + (func $4 (param $0 (ref any)) (result f64) + (unreachable) + ) + (func $5 + (drop + (call $4 + (struct.new $0 + (f64.const 0) + (ref.func $1) + ) + ) + ) + (drop + (call $6) + ) + ) + (func $6 (result f64) + (unreachable) + ) +) +*/ std::unordered_set relevantCallers; for (auto& called : calledByJustUpdated) { auto calledIndex = indexes[called]; @@ -335,9 +385,11 @@ struct DAE : public Pass { } // Clear the old call data before we fill it below. allCalls[calledIndex].clear(); +std::cout << "updating calls to " << called << '\n'; } // Process those callers. for (auto& caller : relevantCallers) { +std::cout << "processing calls from " << caller << '\n'; auto& info = infoMap[caller]; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]]; @@ -446,6 +498,7 @@ struct DAE : public Pass { auto [removedIndexes, outcome] = ParamUtils::removeParameters( {func}, infoMap[name].unusedParams, calls, {}, module, getPassRunner()); if (!removedIndexes.empty()) { +std::cout << "remove param " << func->name << '\n'; // Success! worthOptimizing.insert(func); markStale(name); From b5a7f444ca363bf30f92164f612e1e60b0eb1a12 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 13:10:43 -0800 Subject: [PATCH 04/44] test --- src/passes/DeadArgumentElimination.cpp | 43 ---------------------- test/lit/passes/dae-gc.wast | 50 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 20da87c62eb..a53531a9f46 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -333,49 +333,6 @@ std::cout << "just updated " << func << '\n'; // XXX this is bad... we need to close over repeated operations here, see // $1 here: /* - (module - (rec - (type $S (struct)) - (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) - ) - - (func $0 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) - (unreachable) - ) - (func $1 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) - (unreachable) - ) - (func $2 - (drop - (call $1 - (struct.new $0 - (call $6) - (ref.func $0) - ) - (struct.new_default $S) - ) - ) - ) - (func $4 (param $0 (ref any)) (result f64) - (unreachable) - ) - (func $5 - (drop - (call $4 - (struct.new $0 - (f64.const 0) - (ref.func $1) - ) - ) - ) - (drop - (call $6) - ) - ) - (func $6 (result f64) - (unreachable) - ) -) */ std::unordered_set relevantCallers; for (auto& called : calledByJustUpdated) { diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 6d014d0f8b4..c6ea80ef87f 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -195,3 +195,53 @@ (nop) ) ) + +;; After the first optimization, where we remove params from the call to $1, +;; we update the IR incrementally, and must do so properly: we update $1 and +;; $1's caller $2, and note that $2 also calls $6, so we must update some of +;; $6's callers but not all. +;; TODO: pretty names etc. +(module + (rrec + (type $S (struct)) + (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) + ) + + (func $0 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (unreachable) + ) + (func $1 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (unreachable) + ) + (func $2 + (drop + (call $1 + (struct.new $0 + (call $6) + (ref.func $0) + ) + (struct.new_default $S) + ) + ) + ) + (func $4 (param $0 (ref any)) (result f64) + (unreachable) + ) + (func $5 + (drop + (call $4 + (struct.new $0 + (f64.const 0) + (ref.func $1) + ) + ) + ) + (drop + (call $6) + ) + ) + (func $6 (result f64) + (unreachable) + ) +) + From 36fe4929d318f8db8b1d1cfc7f4383530bdc8e42 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 13:24:01 -0800 Subject: [PATCH 05/44] work --- src/passes/DeadArgumentElimination.cpp | 64 +++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index a53531a9f46..50635e849ca 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -201,7 +201,15 @@ struct DAE : public Pass { std::unordered_map indexes; // TODO comment - std::vector> allCalls; + struct CallInfo { + // Store the calls and their origins in parallel vectors, as we need |calls| + // by itself for certain APIs. This is, origins[i] is the function index in + // which calls[i] appears. + std::vector calls; + std::vector origins; + } + // TODO comment + std::vector allCalls; void run(Module* module) override { DAEFunctionInfoMap infoMap; @@ -315,36 +323,50 @@ std::cout << "iter\n" << *module << '\n'; // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls - // C while B calls nothing, then the list of all calls must be updated for - // D. If D is called by not only A but also some other (not just updated) - // function X, that means we must scan { A, X }. First, find the things - // called by just-updated functions. - std::unordered_set calledByJustUpdated; + // C while B calls C and D, then the list of all calls must be updated for + // C and D. First, find those functions called by just-updated functions. + std::unordered_set justUpdated; + std::unordered_set calledByJustUpdated; for (auto& [func, info] : infoMap) { if (info.justUpdated) { std::cout << "just updated " << func << '\n'; - for (auto& callee : callees[indexes[func]]) { - calledByJustUpdated.insert(module->functions[callee]->name); + auto index = indexes[func]; + justUpdated.insert(index); + for (auto& callee : callees[index]) { + calledByJustUpdated.insert(callee); } } } - // Find all their callers, so we can process the calls from them, thus - // finding all the calls to |calledByJustUpdated|. - // XXX this is bad... we need to close over repeated operations here, see - // $1 here: - /* -*/ + // For each such called function, we don't want to alter calls from + // unchanged functions. That is, if X calls C and D in the example above, + // and X is not just-updated, then X's calls to C and D are fine as they + // are. Leaving such calls alone, remove calls from the callers that we did + // just update, and after that, add them from the fresh data we have on + // those just-updated functions. std::unordered_set relevantCallers; for (auto& called : calledByJustUpdated) { - auto calledIndex = indexes[called]; - for (auto& caller : callers[calledIndex]) { - relevantCallers.insert(caller); + auto& calledCalls = allCalls[called]; + auto oldSize = calledCalls.calls.size(); + assert(oldSize == calledCalls.origins.size()); + Index skip = 0; + for (Index i = 0; i < calledCalls.calls.size(); i++) { + if (justUpdated.count(calledCalls.origins[i])) { + // Remove it by skipping over. + skip++; + } else if (skip) { + // Keep it by writing to the proper place. + calledCalls.calls[i - skip] = calledCalls.calls[i]; + calledCalls.origins[i - skip] = calledCalls.origins[i]; + } + } + if (skip > 0) { + // Update the sizes after removing things. + calledCalls.calls.resize(oldSize - skip); + calledCalls.origins.resize(oldSize - skip); } - // Clear the old call data before we fill it below. - allCalls[calledIndex].clear(); -std::cout << "updating calls to " << called << '\n'; } - // Process those callers. + +XXX // Process those callers. for (auto& caller : relevantCallers) { std::cout << "processing calls from " << caller << '\n'; auto& info = infoMap[caller]; From 6eb2b7efe011f2526fda6c7f54e784e58c3c6efd Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 13:26:12 -0800 Subject: [PATCH 06/44] work --- src/passes/DeadArgumentElimination.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 50635e849ca..cb939551868 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -365,11 +365,11 @@ std::cout << "just updated " << func << '\n'; calledCalls.origins.resize(oldSize - skip); } } - -XXX // Process those callers. - for (auto& caller : relevantCallers) { + // The just-updated callers have been cleaned out of |allCalls|. Add their + // calls, after which that data structure is up-to-date. + for (auto& caller : justUpdated) { + auto& info = infoMap[module->functions[caller]->name]; std::cout << "processing calls from " << caller << '\n'; - auto& info = infoMap[caller]; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]]; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); From f67c5c2b531d799ee043db808bac67bb0a328607 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 14:15:27 -0800 Subject: [PATCH 07/44] comment --- src/passes/DeadArgumentElimination.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index cb939551868..1c346b526ff 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -207,7 +207,7 @@ struct DAE : public Pass { // which calls[i] appears. std::vector calls; std::vector origins; - } + }; // TODO comment std::vector allCalls; @@ -371,7 +371,7 @@ std::cout << "just updated " << func << '\n'; auto& info = infoMap[module->functions[caller]->name]; std::cout << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { - auto& allCallsToName = allCalls[indexes[name]]; + auto& allCallsToName = allCalls[indexes[name]].calls; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); } } @@ -420,7 +420,7 @@ std::cout << "processing calls from " << caller << '\n'; if (hasUnseenCalls[index]) { continue; } - auto& calls = allCalls[index]; + auto& calls = allCalls[index].calls; if (calls.empty()) { // Nothing calls this, so it is not worth optimizing. continue; @@ -469,7 +469,7 @@ std::cout << "processing calls from " << caller << '\n'; if (numParams == 0) { continue; } - auto& calls = allCalls[index]; + auto& calls = allCalls[index].calls; if (calls.empty()) { continue; } @@ -510,7 +510,7 @@ std::cout << "remove param " << func->name << '\n'; if (tailCallees[index]) { continue; } - auto& calls = allCalls[index]; + auto& calls = allCalls[index].calls; if (calls.empty()) { continue; } From d759cb9958746dd6897e82cc7408baf4a121882e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 14:18:23 -0800 Subject: [PATCH 08/44] work --- src/passes/DeadArgumentElimination.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 1c346b526ff..04301262824 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -373,6 +373,11 @@ std::cout << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]].calls; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); + auto num = calls.size(); + auto& origins = allCalls[indexes[name]].origins; + for (Index i = 0; i < num; i++) { + origins.push_back(caller); + } } } From 6007366bd905e7e06f9e0e9c2311d29bdefbac0a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 14:19:00 -0800 Subject: [PATCH 09/44] comment --- src/passes/DeadArgumentElimination.cpp | 8 +- test/lit/passes/dae-gc-refine-params.wast | 97 +- test/lit/passes/dae-gc-refine-return.wast | 32 +- test/lit/passes/dae-gc.wast | 191 +- test/lit/passes/dae-optimizing.wast | 140 +- .../dae-refine-params-and-optimize.wast | 19 +- test/lit/passes/dae_all-features.wast | 1733 +++++++++++++---- test/lit/passes/dae_tnh.wast | 72 +- 8 files changed, 1660 insertions(+), 632 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 04301262824..e1f67e653b0 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -319,7 +319,7 @@ struct DAE : public Pass { } } -std::cout << "iter\n" << *module << '\n'; +std::cerr << "iter\n" << *module << '\n'; // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls @@ -329,7 +329,7 @@ std::cout << "iter\n" << *module << '\n'; std::unordered_set calledByJustUpdated; for (auto& [func, info] : infoMap) { if (info.justUpdated) { -std::cout << "just updated " << func << '\n'; +std::cerr << "just updated " << func << '\n'; auto index = indexes[func]; justUpdated.insert(index); for (auto& callee : callees[index]) { @@ -369,7 +369,7 @@ std::cout << "just updated " << func << '\n'; // calls, after which that data structure is up-to-date. for (auto& caller : justUpdated) { auto& info = infoMap[module->functions[caller]->name]; -std::cout << "processing calls from " << caller << '\n'; +std::cerr << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]].calls; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); @@ -482,7 +482,7 @@ std::cout << "processing calls from " << caller << '\n'; auto [removedIndexes, outcome] = ParamUtils::removeParameters( {func}, infoMap[name].unusedParams, calls, {}, module, getPassRunner()); if (!removedIndexes.empty()) { -std::cout << "remove param " << func->name << '\n'; +std::cerr << "remove param " << func->name << '\n'; // Success! worthOptimizing.insert(func); markStale(name); diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast index 27f9a214d11..f627f116a67 100644 --- a/test/lit/passes/dae-gc-refine-params.wast +++ b/test/lit/passes/dae-gc-refine-params.wast @@ -19,7 +19,7 @@ (type $"{i32_f32}" (sub $"{i32}" (struct (field i32) (field f32)))) - ;; CHECK: (func $call-various-params-no (type $2) + ;; CHECK: (func $call-various-params-no (type $1) ;; CHECK-NEXT: (call $various-params-no ;; CHECK-NEXT: (call $"get_{}") ;; CHECK-NEXT: (call $"get_{i32}") @@ -45,7 +45,7 @@ ) ;; This function is called in ways that do not allow us to alter the types of ;; its parameters (see last function). - ;; CHECK: (func $various-params-no (type $8) (param $x (ref null $"{}")) (param $y (ref null $"{}")) + ;; CHECK: (func $various-params-no (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -59,26 +59,26 @@ (drop (local.get $y)) ) - ;; CHECK: (func $"get_{}" (type $9) (result (ref null $"{}")) + ;; CHECK: (func $"get_{}" (type $10) (result (ref null $"{}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{}" (result (ref null $"{}")) (unreachable) ) - ;; CHECK: (func $"get_{i32}" (type $5) (result (ref null $"{i32}")) + ;; CHECK: (func $"get_{i32}" (type $6) (result (ref null $"{i32}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{i32}" (result (ref null $"{i32}")) (unreachable) ) - ;; CHECK: (func $"get_{f64}" (type $10) (result (ref null $"{f64}")) + ;; CHECK: (func $"get_{f64}" (type $11) (result (ref null $"{f64}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{f64}" (result (ref null $"{f64}")) (unreachable) ) - ;; CHECK: (func $call-various-params-yes (type $2) + ;; CHECK: (func $call-various-params-yes (type $1) ;; CHECK-NEXT: (call $various-params-yes ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: (i32.const 0) @@ -107,7 +107,7 @@ ) ;; This function is called in ways that *do* allow us to alter the types of ;; its parameters (see last function). - ;; CHECK: (func $various-params-yes (type $11) (param $x (ref null $"{i32}")) (param $i i32) (param $y (ref null $"{i32}")) + ;; CHECK: (func $various-params-yes (type $12) (param $x (ref null $"{}")) (param $i i32) (param $y (ref null $"{}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -125,7 +125,7 @@ (drop (local.get $y)) ) - ;; CHECK: (func $call-various-params-set (type $2) + ;; CHECK: (func $call-various-params-set (type $1) ;; CHECK-NEXT: (call $various-params-set ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: (call $"get_null_{i32}") @@ -150,30 +150,24 @@ ;; This function is called in ways that *do* allow us to alter the types of ;; its parameters (see last function), however, we reuse the parameters by ;; writing to them, which causes problems in one case. - ;; CHECK: (func $various-params-set (type $12) (param $x (ref null $"{i32}")) (param $y (ref null $"{i32}")) - ;; CHECK-NEXT: (local $2 (ref null $"{}")) - ;; CHECK-NEXT: (local.set $2 + ;; CHECK: (func $various-params-set (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (struct.new_default $"{}") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $y - ;; CHECK-NEXT: (call $"get_null_{i32_i64}") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (struct.new_default $"{}") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $y + ;; CHECK-NEXT: (call $"get_null_{i32_i64}") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $various-params-set (param $x (ref null $"{}")) (param $y (ref null $"{}")) @@ -196,7 +190,7 @@ ) ) - ;; CHECK: (func $call-various-params-tee (type $2) + ;; CHECK: (func $call-various-params-tee (type $1) ;; CHECK-NEXT: (call $various-params-tee ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: ) @@ -207,12 +201,12 @@ (call $"get_null_{i32}") ) ) - ;; CHECK: (func $various-params-tee (type $6) (param $x (ref null $"{i32}")) + ;; CHECK: (func $various-params-tee (type $7) (param $x (ref null $"{}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref null $"{i32}")) + ;; CHECK-NEXT: (block (result (ref null $"{}")) ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (call $"get_null_{i32_i64}") ;; CHECK-NEXT: ) @@ -232,7 +226,7 @@ ) ) - ;; CHECK: (func $call-various-params-null (type $2) + ;; CHECK: (func $call-various-params-null (type $1) ;; CHECK-NEXT: (call $various-params-null ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (ref.null none) @@ -262,7 +256,7 @@ ) ;; This function is called in ways that allow us to make the first parameter ;; non-nullable. - ;; CHECK: (func $various-params-null (type $13) (param $x (ref none)) (param $y (ref null $"{i32}")) + ;; CHECK: (func $various-params-null (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) @@ -286,7 +280,7 @@ (local.set $temp (local.get $temp)) ) - ;; CHECK: (func $call-various-params-middle (type $2) + ;; CHECK: (func $call-various-params-middle (type $1) ;; CHECK-NEXT: (call $various-params-middle ;; CHECK-NEXT: (call $"get_null_{i32_i64}") ;; CHECK-NEXT: ) @@ -304,7 +298,7 @@ (call $"get_null_{i32_f32}") ) ) - ;; CHECK: (func $various-params-middle (type $6) (param $x (ref null $"{i32}")) + ;; CHECK: (func $various-params-middle (type $7) (param $x (ref null $"{}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -314,8 +308,7 @@ (drop (local.get $x)) ) - ;; CHECK: (func $unused-and-refinable (type $2) - ;; CHECK-NEXT: (local $0 structref) + ;; CHECK: (func $unused-and-refinable (type $8) (param $0 structref) ;; CHECK-NEXT: ) (func $unused-and-refinable (param $0 structref) ;; This function does not use $0. It is called with $"{}", so it is also @@ -329,8 +322,10 @@ ;; local). ) - ;; CHECK: (func $call-unused-and-refinable (type $2) - ;; CHECK-NEXT: (call $unused-and-refinable) + ;; CHECK: (func $call-unused-and-refinable (type $1) + ;; CHECK-NEXT: (call $unused-and-refinable + ;; CHECK-NEXT: (struct.new_default $"{}") + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $call-unused-and-refinable (call $unused-and-refinable @@ -338,14 +333,10 @@ ) ) - ;; CHECK: (func $non-nullable-fixup (type $14) (param $0 (ref (exact $"{}"))) - ;; CHECK-NEXT: (local $1 structref) - ;; CHECK-NEXT: (local.set $1 + ;; CHECK: (func $non-nullable-fixup (type $8) (param $0 structref) + ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $non-nullable-fixup (param $0 structref) ;; Use the param to avoid other opts removing it, and to force us to do a @@ -356,7 +347,7 @@ ) ) - ;; CHECK: (func $call-non-nullable-fixup (type $2) + ;; CHECK: (func $call-non-nullable-fixup (type $1) ;; CHECK-NEXT: (call $non-nullable-fixup ;; CHECK-NEXT: (struct.new_default $"{}") ;; CHECK-NEXT: ) @@ -367,7 +358,7 @@ ) ) - ;; CHECK: (func $call-update-null (type $2) + ;; CHECK: (func $call-update-null (type $1) ;; CHECK-NEXT: (call $update-null ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -386,7 +377,7 @@ ) ) - ;; CHECK: (func $update-null (type $15) (param $x (ref null (exact $"{}"))) + ;; CHECK: (func $update-null (type $13) (param $x anyref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -398,7 +389,7 @@ (drop (local.get $x)) ) - ;; CHECK: (func $"get_null_{i32}" (type $5) (result (ref null $"{i32}")) + ;; CHECK: (func $"get_null_{i32}" (type $6) (result (ref null $"{i32}")) ;; CHECK-NEXT: (select (result (ref null $"{i32}")) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (select (result (ref $"{i32}")) @@ -425,7 +416,7 @@ ) ) - ;; CHECK: (func $"get_null_{i32_i64}" (type $16) (result (ref null (exact $"{i32_i64}"))) + ;; CHECK: (func $"get_null_{i32_i64}" (type $14) (result (ref null $"{i32_i64}")) ;; CHECK-NEXT: (select (result (ref null (exact $"{i32_i64}"))) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (struct.new_default $"{i32_i64}") @@ -440,7 +431,7 @@ ) ) - ;; CHECK: (func $"get_null_{i32_f32}" (type $17) (result (ref null (exact $"{i32_f32}"))) + ;; CHECK: (func $"get_null_{i32_f32}" (type $15) (result (ref null $"{i32_f32}")) ;; CHECK-NEXT: (select (result (ref null (exact $"{i32_f32}"))) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (struct.new_default $"{i32_f32}") diff --git a/test/lit/passes/dae-gc-refine-return.wast b/test/lit/passes/dae-gc-refine-return.wast index 1ef7bc1b13c..bd3aeef5cc9 100644 --- a/test/lit/passes/dae-gc-refine-return.wast +++ b/test/lit/passes/dae-gc-refine-return.wast @@ -55,7 +55,7 @@ ) ;; Refine the return type based on the value flowing out. - ;; CHECK: (func $refine-return-flow (type $4) (result i31ref) + ;; CHECK: (func $refine-return-flow (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -71,12 +71,12 @@ (local.get $i31) ) - ;; CHECK: (func $call-refine-return-flow (type $4) (result i31ref) + ;; CHECK: (func $call-refine-return-flow (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (call $call-refine-return-flow) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if (result i31ref) + ;; CHECK-NEXT: (if (result anyref) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (call $refine-return-flow) @@ -105,7 +105,7 @@ ) ;; Refine the return type based on a return. - ;; CHECK: (func $refine-return-return (type $4) (result i31ref) + ;; CHECK: (func $refine-return-return (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -125,7 +125,7 @@ ) ;; Refine the return type based on multiple values. - ;; CHECK: (func $refine-return-many (type $4) (result i31ref) + ;; CHECK: (func $refine-return-many (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -170,7 +170,7 @@ (local.get $i31) ) - ;; CHECK: (func $refine-return-many-lub (type $7) (result eqref) + ;; CHECK: (func $refine-return-many-lub (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local $struct structref) @@ -218,7 +218,7 @@ (local.get $i31) ) - ;; CHECK: (func $refine-return-many-lub-2 (type $7) (result eqref) + ;; CHECK: (func $refine-return-many-lub-2 (type $0) (result anyref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local $struct structref) @@ -267,7 +267,7 @@ ) ;; We can refine the return types of tuples. - ;; CHECK: (func $refine-return-tuple (type $8) (result i31ref i32) + ;; CHECK: (func $refine-return-tuple (type $5) (result anyref i32) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -306,7 +306,7 @@ (func $do-return-call (result funcref) (return_call $return-ref-func) ) - ;; CHECK: (func $return-ref-func (type $9) (result (ref (exact $6))) + ;; CHECK: (func $return-ref-func (type $6) (result funcref) ;; CHECK-NEXT: (ref.func $do-return-call) ;; CHECK-NEXT: ) (func $return-ref-func (result funcref) @@ -321,7 +321,7 @@ (func $tail-callee (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-yes (type $"return_{}") (result (ref $"{}")) + ;; CHECK: (func $tail-caller-yes (type $0) (result anyref) ;; CHECK-NEXT: (return_call $tail-callee) ;; CHECK-NEXT: ) (func $tail-caller-yes (result anyref) @@ -353,7 +353,7 @@ ) (return_call $tail-callee) ) - ;; CHECK: (func $tail-call-caller (type $5) + ;; CHECK: (func $tail-call-caller (type $3) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-yes) ;; CHECK-NEXT: ) @@ -378,7 +378,7 @@ (func $tail-callee-indirect (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-indirect-yes (type $"return_{}") (result (ref $"{}")) + ;; CHECK: (func $tail-caller-indirect-yes (type $0) (result anyref) ;; CHECK-NEXT: (return_call_indirect $0 (type $"return_{}") ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -410,7 +410,7 @@ ) (return_call_indirect (type $"return_{}") (i32.const 0)) ) - ;; CHECK: (func $tail-call-caller-indirect (type $5) + ;; CHECK: (func $tail-call-caller-indirect (type $3) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-indirect-yes) ;; CHECK-NEXT: ) @@ -434,7 +434,7 @@ (func $tail-callee-call_ref (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-call_ref-yes (type $"return_{}") (result (ref $"{}")) + ;; CHECK: (func $tail-caller-call_ref-yes (type $0) (result anyref) ;; CHECK-NEXT: (local $"return_{}" (ref null $"return_{}")) ;; CHECK-NEXT: (return_call_ref $"return_{}" ;; CHECK-NEXT: (local.get $"return_{}") @@ -484,7 +484,7 @@ ;; should not hit an assertion on such things. (return_call_ref $"return_{}" (unreachable)) ) - ;; CHECK: (func $tail-call-caller-call_ref (type $5) + ;; CHECK: (func $tail-call-caller-call_ref (type $3) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-call_ref-yes) ;; CHECK-NEXT: ) @@ -507,7 +507,7 @@ ) ) - ;; CHECK: (func $update-null (type $10) (param $x i32) (param $y i32) (result (ref null $"{i32}")) + ;; CHECK: (func $update-null (type $7) (param $x i32) (param $y i32) (result anyref) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (then diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index c6ea80ef87f..4f0714ce9dd 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -5,8 +5,12 @@ ;; CHECK: (type $"{}" (struct)) (type $"{}" (struct)) - ;; CHECK: (func $foo (type $0) - ;; CHECK-NEXT: (call $bar) + ;; CHECK: (func $foo (type $1) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (ref.i31 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $foo (call $bar @@ -15,8 +19,7 @@ ) ) ) - ;; CHECK: (func $bar (type $0) - ;; CHECK-NEXT: (local $0 i31ref) + ;; CHECK: (func $bar (type $2) (param $0 i31ref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $0 ;; CHECK-NEXT: (ref.i31 @@ -49,15 +52,16 @@ ) ;; A function that gets a non-nullable reference that is never used. We can ;; still create a non-nullable local for that parameter. - ;; CHECK: (func $get-nonnull (type $0) - ;; CHECK-NEXT: (local $0 (ref $"{}")) + ;; CHECK: (func $get-nonnull (type $3) (param $0 (ref $"{}")) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $get-nonnull (param $0 (ref $"{}")) (nop) ) - ;; CHECK: (func $send-nonnull (type $0) - ;; CHECK-NEXT: (call $get-nonnull) + ;; CHECK: (func $send-nonnull (type $1) + ;; CHECK-NEXT: (call $get-nonnull + ;; CHECK-NEXT: (struct.new_default $"{}") + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $send-nonnull (call $get-nonnull @@ -68,19 +72,12 @@ ;; Test ref.func and ref.null optimization of constant parameter values. (module - ;; CHECK: (func $foo (type $1) (param $0 (ref (exact $0))) - ;; CHECK-NEXT: (local $1 (ref (exact $0))) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (ref.func $a) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $"{}" (struct)) + + ;; CHECK: (func $foo (type $0) + ;; CHECK-NEXT: (call $bar) ;; CHECK-NEXT: ) (func $foo (param $x (ref func)) (param $y (ref func)) ;; "Use" the params to avoid other optimizations kicking in. @@ -88,14 +85,6 @@ (drop (local.get $y)) ) - ;; CHECK: (func $call-foo (type $0) - ;; CHECK-NEXT: (call $foo - ;; CHECK-NEXT: (ref.func $b) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $foo - ;; CHECK-NEXT: (ref.func $c) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $call-foo ;; Call $foo with a constant function in the first param, which we ;; can optimize, but different ones in the second. @@ -109,19 +98,18 @@ ) ) - ;; CHECK: (func $bar (type $2) (param $0 i31ref) - ;; CHECK-NEXT: (local $1 nullref) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (ref.null none) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) + ;; CHECK: (func $bar (type $0) + ;; CHECK-NEXT: (local $0 i31ref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (ref.i31 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $bar (param $x (ref null any)) (param $y (ref null any)) ;; "Use" the params to avoid other optimizations kicking in. @@ -129,16 +117,6 @@ (drop (local.get $y)) ) - ;; CHECK: (func $call-bar (type $0) - ;; CHECK-NEXT: (call $bar - ;; CHECK-NEXT: (ref.null none) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $bar - ;; CHECK-NEXT: (ref.i31 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $call-bar ;; Call with nulls. Mixing nulls is fine as they all have the same type and ;; value. However, mixing a null with a reference stops us in the second @@ -154,41 +132,27 @@ ) ;; Helper functions so we have something to take the reference of. - ;; CHECK: (func $a (type $0) - ;; CHECK-NEXT: ) (func $a) - ;; CHECK: (func $b (type $0) - ;; CHECK-NEXT: ) (func $b) - ;; CHECK: (func $c (type $0) - ;; CHECK-NEXT: ) (func $c) ) ;; Test that string constants can be applied. +;; CHECK: (func $get-nonnull (type $0) +;; CHECK-NEXT: (local $0 (ref $"{}")) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) + +;; CHECK: (func $send-nonnull (type $0) +;; CHECK-NEXT: (call $get-nonnull) +;; CHECK-NEXT: ) (module - ;; CHECK: (func $0 (type $0) - ;; CHECK-NEXT: (call $1) - ;; CHECK-NEXT: ) (func $0 (call $1 (string.const "310") (string.const "929") ) ) - ;; CHECK: (func $1 (type $0) - ;; CHECK-NEXT: (local $0 (ref string)) - ;; CHECK-NEXT: (local $1 (ref string)) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (string.const "929") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (string.const "310") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $1 (param $0 (ref string)) (param $1 (ref string)) ;; The parameters here will be removed, and the constant values placed in the ;; function. @@ -201,9 +165,40 @@ ;; $1's caller $2, and note that $2 also calls $6, so we must update some of ;; $6's callers but not all. ;; TODO: pretty names etc. +;; CHECK: (type $0 (func)) + +;; CHECK: (type $"{}" (struct)) + +;; CHECK: (func $foo (type $0) +;; CHECK-NEXT: (call $bar) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $0) +;; CHECK-NEXT: (local $0 i31ref) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.tee $0 +;; CHECK-NEXT: (ref.i31 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.tee $0 +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $get-nonnull (type $0) +;; CHECK-NEXT: (local $0 (ref $"{}")) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) + +;; CHECK: (func $send-nonnull (type $0) +;; CHECK-NEXT: (call $get-nonnull) +;; CHECK-NEXT: ) (module - (rrec + (rec (type $S (struct)) + ;; CHECK: (type $0 (func)) (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) ) @@ -245,3 +240,53 @@ ) ) +;; CHECK: (func $foo (type $1) (param $x (ref func)) (param $y (ref func)) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $y) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $call-foo (type $0) +;; CHECK-NEXT: (call $foo +;; CHECK-NEXT: (ref.func $a) +;; CHECK-NEXT: (ref.func $b) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $foo +;; CHECK-NEXT: (ref.func $a) +;; CHECK-NEXT: (ref.func $c) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $2) (param $x anyref) (param $y anyref) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $y) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $call-bar (type $0) +;; CHECK-NEXT: (call $bar +;; CHECK-NEXT: (ref.null none) +;; CHECK-NEXT: (ref.null none) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $bar +;; CHECK-NEXT: (ref.null none) +;; CHECK-NEXT: (ref.i31 +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: ) + +;; CHECK: (func $c (type $0) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae-optimizing.wast b/test/lit/passes/dae-optimizing.wast index 316c377586f..d61f2d51152 100644 --- a/test/lit/passes/dae-optimizing.wast +++ b/test/lit/passes/dae-optimizing.wast @@ -4,48 +4,55 @@ ;; RUN: foreach %s %t wasm-opt -all --dae-optimizing -S -o - | filecheck %s (module + ;; CHECK: (type $1 (func (param f64 f32 f32 f64 f32 i64 f64) (result i32))) + + ;; CHECK: (type $0 (func (param f32) (result f32))) (type $0 (func (param f32) (result f32))) (type $1 (func (param f64 f32 f32 f64 f32 i64 f64) (result i32))) - ;; CHECK: (type $3 (func (result i32))) - - ;; CHECK: (type $4 (func (result f32))) - ;; CHECK: (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) ;; CHECK: (global $global$0 (mut i32) (i32.const 10)) (global $global$0 (mut i32) (i32.const 10)) - ;; CHECK: (func $0 (type $3) (result i32) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result f32) - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (i32.const 33554432) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then + ;; CHECK: (func $0 (type $1) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i64) (param $6 f64) (result i32) + ;; CHECK-NEXT: (local $7 i32) + ;; CHECK-NEXT: (local $8 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.tee $7 + ;; CHECK-NEXT: (i32.const 33554432) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (loop $label$2 (result f32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (global.get $global$0) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (block $label$4 (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $7 + ;; CHECK-NEXT: (local.get $8) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br_if $label$2 - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (f32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (call $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $1 + ;; CHECK-NEXT: (f32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -96,14 +103,22 @@ ) (i32.const -11) ) - ;; CHECK: (func $1 (type $4) (result f32) + ;; CHECK: (func $1 (type $0) (param $0 f32) (result f32) ;; CHECK-NEXT: (f32.const 0) ;; CHECK-NEXT: ) (func $1 (; 1 ;) (type $0) (param $0 f32) (result f32) (f32.const 0) ) ;; CHECK: (func $2 (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) - ;; CHECK-NEXT: (call $0) + ;; CHECK-NEXT: (call $0 + ;; CHECK-NEXT: (f64.const 1) + ;; CHECK-NEXT: (f32.const 1) + ;; CHECK-NEXT: (f32.const 1) + ;; CHECK-NEXT: (f64.const 1) + ;; CHECK-NEXT: (f32.const 1) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (f64.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $2 (; 2 ;) (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) (call $0 @@ -122,22 +137,6 @@ (module (func $import (import "" "") (result i32)) (func $impossible (import "" "") (result (ref none))) - ;; CHECK: (type $0 (func (result i32))) - - ;; CHECK: (type $1 (func (result (ref none)))) - - ;; CHECK: (type $2 (func)) - - ;; CHECK: (import "" "" (func $import (type $0) (result i32))) - - ;; CHECK: (import "" "" (func $impossible (type $1) (result (ref none)))) - - ;; CHECK: (export "export" (func $export)) - - ;; CHECK: (func $export (type $0) (result i32) - ;; CHECK-NEXT: (call $internal) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) (func $export (export "export") (result i32) (drop ;; This should be optimized to an unreachable sequence. @@ -153,14 +152,6 @@ ) (i32.const 0) ) - ;; CHECK: (func $internal (type $2) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $import) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $impossible) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $internal (param i32) (result (ref none)) ;; Prevent this from being removed entirely. (drop @@ -169,3 +160,56 @@ (call $impossible) ) ) +;; CHECK: (type $3 (func (result i32))) + +;; CHECK: (type $4 (func (result f32))) + +;; CHECK: (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) + +;; CHECK: (global $global$0 (mut i32) (i32.const 10)) + +;; CHECK: (func $0 (type $3) (result i32) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (if (result f32) +;; CHECK-NEXT: (local.tee $0 +;; CHECK-NEXT: (i32.const 33554432) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (loop $label$2 (result f32) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (global.get $global$0) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (return +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (br_if $label$2 +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (f32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (else +;; CHECK-NEXT: (call $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const -11) +;; CHECK-NEXT: ) + +;; CHECK: (func $1 (type $4) (result f32) +;; CHECK-NEXT: (f32.const 0) +;; CHECK-NEXT: ) + +;; CHECK: (func $2 (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) +;; CHECK-NEXT: (call $0) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae-refine-params-and-optimize.wast b/test/lit/passes/dae-refine-params-and-optimize.wast index 9d893c21e54..766af22d38e 100644 --- a/test/lit/passes/dae-refine-params-and-optimize.wast +++ b/test/lit/passes/dae-refine-params-and-optimize.wast @@ -4,16 +4,23 @@ ;; The br_on_cast_fail is optimized away thanks to the refined type. The output still has some unoptimized code (an additional --dce pass would get rid of the drop-return pattern here), but that is not directly relevant to this test. (module - ;; CHECK: (type $0 (func (param (ref array)) (result i32))) + ;; CHECK: (type $0 (func (param (ref eq)) (result i32))) - ;; CHECK: (func $len (type $0) (param $0 (ref array)) (result i32) + ;; CHECK: (type $1 (func (param (ref array)) (result i32))) + + ;; CHECK: (func $len (type $0) (param $0 (ref eq)) (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (array.len - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (block $not_array (result (ref eq)) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (array.len + ;; CHECK-NEXT: (br_on_cast_fail $not_array (ref eq) (ref array) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: ) (func $len (param (ref eq)) (result i32) (drop @@ -29,7 +36,7 @@ ) (i32.const -1) ) - ;; CHECK: (func $optimize-after-refinement (type $0) (param $0 (ref array)) (result i32) + ;; CHECK: (func $optimize-after-refinement (type $1) (param $0 (ref array)) (result i32) ;; CHECK-NEXT: (call $len ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae_all-features.wast b/test/lit/passes/dae_all-features.wast index 145ae1ff032..2158d7c23e7 100644 --- a/test/lit/passes/dae_all-features.wast +++ b/test/lit/passes/dae_all-features.wast @@ -11,13 +11,15 @@ ;; CHECK: (type $2 (func (result i32))) - ;; CHECK: (type $3 (func (result f64))) + ;; CHECK: (type $3 (func (param i32 f64))) - ;; CHECK: (type $4 (func (param f64))) + ;; CHECK: (type $4 (func (result f64))) + + ;; CHECK: (type $5 (func (param i32) (result i32))) ;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) (import "a" "b" (func $get-i32 (result i32))) - ;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) + ;; CHECK: (import "a" "c" (func $get-f64 (type $4) (result f64))) (import "a" "c" (func $get-f64 (result f64))) ;; CHECK: (table $0 2 2 funcref) @@ -28,39 +30,35 @@ (export "a8" (func $a8)) (table 2 2 funcref) (elem (i32.const 0) $a9 $c8) - ;; CHECK: (func $a (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: ) + ;; CHECK: (func $a (type $1) (param $x i32) ;; CHECK-NEXT: ) (func $a (param $x i32)) ;; CHECK: (func $b (type $0) - ;; CHECK-NEXT: (call $a) + ;; CHECK-NEXT: (call $a + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b (call $a (i32.const 1)) ;; best case scenario ) - ;; CHECK: (func $a1 (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK: (func $a1 (type $1) (param $x i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $a1 (param $x i32) (unreachable) ) ;; CHECK: (func $b1 (type $0) - ;; CHECK-NEXT: (call $a1) + ;; CHECK-NEXT: (call $a1 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b1 (call $a1 (i32.const 2)) ;; same value in both, so works ) ;; CHECK: (func $b11 (type $0) - ;; CHECK-NEXT: (call $a1) + ;; CHECK-NEXT: (call $a1 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b11 (call $a1 (i32.const 2)) @@ -89,8 +87,7 @@ (func $b22 (call $a2 (i32.const 4)) ) - ;; CHECK: (func $a3 (type $0) - ;; CHECK-NEXT: (local $0 i32) + ;; CHECK: (func $a3 (type $1) (param $x i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: ) @@ -99,31 +96,31 @@ (drop (i32.const -1)) ;; diff value, but at least unused, so no need to send ) ;; CHECK: (func $b3 (type $0) - ;; CHECK-NEXT: (call $a3) + ;; CHECK-NEXT: (call $a3 + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b3 (call $a3 (i32.const 3)) ) ;; CHECK: (func $b33 (type $0) - ;; CHECK-NEXT: (call $a3) + ;; CHECK-NEXT: (call $a3 + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b33 (call $a3 (i32.const 4)) ) - ;; CHECK: (func $a4 (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: ) + ;; CHECK: (func $a4 (type $1) (param $x i32) ;; CHECK-NEXT: ) (func $a4 (param $x i32) ;; This function is called with one constant and one unreachable. We can ;; remove the param despite the unreachable's effects. ) ;; CHECK: (func $b4 (type $0) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (call $a4 + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b4 ;; This call will vanish entirely, because the unreachable child executes @@ -132,30 +129,20 @@ (call $a4 (unreachable)) ) ;; CHECK: (func $b43 (type $0) - ;; CHECK-NEXT: (call $a4) + ;; CHECK-NEXT: (call $a4 + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b43 ;; We will remove the parameter here. (call $a4 (i32.const 4)) ) - ;; CHECK: (func $a5 (type $0) - ;; CHECK-NEXT: (local $0 f64) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (f64.const 3.14159) + ;; CHECK: (func $a5 (type $3) (param $x i32) (param $y f64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a5 (param $x i32) (param $y f64) ;; optimize two @@ -163,23 +150,20 @@ (drop (local.get $y)) ) ;; CHECK: (func $b5 (type $0) - ;; CHECK-NEXT: (call $a5) + ;; CHECK-NEXT: (call $a5 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (f64.const 3.14159) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b5 (call $a5 (i32.const 1) (f64.const 3.14159)) ) - ;; CHECK: (func $a6 (type $1) (param $0 i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (f64.const 3.14159) + ;; CHECK: (func $a6 (type $3) (param $x i32) (param $y f64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a6 (param $x i32) (param $y f64) ;; optimize just one @@ -189,23 +173,18 @@ ;; CHECK: (func $b6 (type $0) ;; CHECK-NEXT: (call $a6 ;; CHECK-NEXT: (call $get-i32) + ;; CHECK-NEXT: (f64.const 3.14159) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b6 (call $a6 (call $get-i32) (f64.const 3.14159)) ) - ;; CHECK: (func $a7 (type $4) (param $0 f64) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK: (func $a7 (type $3) (param $x i32) (param $y f64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a7 (param $x i32) (param $y f64) ;; optimize just the other one @@ -214,6 +193,7 @@ ) ;; CHECK: (func $b7 (type $0) ;; CHECK-NEXT: (call $a7 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (call $get-f64) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -242,24 +222,25 @@ (func $b9 (call $a9 (i32.const 1)) ) - ;; CHECK: (func $a10 (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK: (func $a10 (type $1) (param $x i32) + ;; CHECK-NEXT: (call $a10 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (call $a10) - ;; CHECK-NEXT: (call $a10) + ;; CHECK-NEXT: (call $a10 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a10 (param $x i32) ;; recursion (call $a10 (i32.const 1)) (call $a10 (i32.const 1)) ) - ;; CHECK: (func $a11 (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (call $a11) - ;; CHECK-NEXT: (call $a11) + ;; CHECK: (func $a11 (type $1) (param $x i32) + ;; CHECK-NEXT: (call $a11 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a11 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a11 (param $x i32) ;; partially successful recursion (call $a11 (i32.const 1)) @@ -284,20 +265,32 @@ ;; return values ;; CHECK: (func $c1 (type $0) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (call $c2) - ;; CHECK-NEXT: (call $c3) - ;; CHECK-NEXT: (call $c3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $c4) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (call $c4) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $c5 - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c5 + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $c7) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $c6) - ;; CHECK-NEXT: (call $c7) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $c8) ;; CHECK-NEXT: ) @@ -314,18 +307,14 @@ (drop (call $c7)) (drop (call $c8)) ) - ;; CHECK: (func $c2 (type $0) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK: (func $c2 (type $2) (result i32) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) (func $c2 (result i32) (i32.const 1) ) - ;; CHECK: (func $c3 (type $0) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK: (func $c3 (type $2) (result i32) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) (func $c3 (result i32) (i32.const 2) @@ -336,25 +325,22 @@ (func $c4 (result i32) (i32.const 3) ) - ;; CHECK: (func $c5 (type $1) (param $x i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) + ;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) (func $c5 (param $x i32) (result i32) (local.get $x) ) - ;; CHECK: (func $c6 (type $0) + ;; CHECK: (func $c6 (type $2) (result i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $c6 (result i32) (unreachable) ) - ;; CHECK: (func $c7 (type $0) - ;; CHECK-NEXT: (drop + ;; CHECK: (func $c7 (type $2) (result i32) + ;; CHECK-NEXT: (return ;; CHECK-NEXT: (i32.const 4) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) (func $c7 (result i32) (return (i32.const 4)) @@ -369,10 +355,33 @@ (module ;; both operations at once: remove params and return value ;; CHECK: (type $0 (func)) - ;; CHECK: (export "a" (func $a)) + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (type $2 (func (param i32))) + + ;; CHECK: (type $3 (func (result f64))) + + ;; CHECK: (type $4 (func (param f64))) + + ;; CHECK: (type $5 (func (param i32) (result i32))) + + ;; CHECK: (import "a" "b" (func $get-i32 (type $1) (result i32))) + + ;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) + + ;; CHECK: (table $0 2 2 funcref) + + ;; CHECK: (elem $0 (i32.const 0) $a9 $c8) + + ;; CHECK: (export "a8" (func $a8)) ;; CHECK: (func $a (type $0) - ;; CHECK-NEXT: (call $b) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a (export "a") (drop @@ -382,31 +391,251 @@ ) ) ;; CHECK: (func $b (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a) ;; CHECK-NEXT: ) (func $b (param $x i32) (result i32) (local.get $x) ) ) -(module ;; tail calls inhibit dropped result removal - ;; CHECK: (type $0 (func (param i32) (result i32))) +;; CHECK: (func $a1 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) - ;; CHECK: (type $1 (func (result i32))) +;; CHECK: (func $b1 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) - ;; CHECK: (func $foo (type $0) (param $x i32) (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (return_call $bar) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) +;; CHECK: (func $b11 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $a2 (type $2) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b2 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b22 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a3 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const -1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b3 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $b33 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $a4 (type $2) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b4 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b43 (type $0) +;; CHECK-NEXT: (call $a4 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a5 (type $0) +;; CHECK-NEXT: (local $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b5 (type $0) +;; CHECK-NEXT: (call $a5) +;; CHECK-NEXT: ) + +;; CHECK: (func $a6 (type $2) (param $0 i32) +;; CHECK-NEXT: (local $1 f64) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b6 (type $0) +;; CHECK-NEXT: (call $a6 +;; CHECK-NEXT: (call $get-i32) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a7 (type $4) (param $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b7 (type $0) +;; CHECK-NEXT: (call $a7 +;; CHECK-NEXT: (call $get-f64) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a8 (type $2) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b8 (type $0) +;; CHECK-NEXT: (call $a8 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a9 (type $2) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b9 (type $0) +;; CHECK-NEXT: (call $a9 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a10 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a11 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: ) + +;; CHECK: (func $a12 (type $2) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c1 (type $0) +;; CHECK-NEXT: (local $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $x +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c5 +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c6) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c7) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c8) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c2 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) + +;; CHECK: (func $c3 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) + +;; CHECK: (func $c4 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) + +;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) + +;; CHECK: (func $c6 (type $1) (result i32) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $c7 (type $1) (result i32) +;; CHECK-NEXT: (return +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c8 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 5) +;; CHECK-NEXT: ) +(module ;; tail calls inhibit dropped result removal (func $foo (param $x i32) (result i32) (drop (return_call $bar @@ -415,36 +644,290 @@ ) (i32.const 42) ) - ;; CHECK: (func $bar (type $1) (result i32) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 7) - ;; CHECK-NEXT: ) (func $bar (param $x i32) (result i32) (i32.const 7) ) ) +;; CHECK: (type $0 (func)) + +;; CHECK: (type $1 (func (result i32))) + +;; CHECK: (type $2 (func (param i32))) + +;; CHECK: (type $3 (func (result f64))) + +;; CHECK: (type $4 (func (param f64))) + +;; CHECK: (type $5 (func (param i32) (result i32))) + +;; CHECK: (import "a" "b" (func $get-i32 (type $1) (result i32))) + +;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) + +;; CHECK: (table $0 2 2 funcref) + +;; CHECK: (elem $0 (i32.const 0) $a9 $c8) + +;; CHECK: (export "a8" (func $a8)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: (call $a) +;; CHECK-NEXT: ) + +;; CHECK: (func $a1 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b1 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $b11 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $a2 (type $2) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b2 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b22 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a3 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const -1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b3 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $b33 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $a4 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b4 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b43 (type $0) +;; CHECK-NEXT: (call $a4) +;; CHECK-NEXT: ) + +;; CHECK: (func $a5 (type $0) +;; CHECK-NEXT: (local $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b5 (type $0) +;; CHECK-NEXT: (call $a5) +;; CHECK-NEXT: ) + +;; CHECK: (func $a6 (type $2) (param $0 i32) +;; CHECK-NEXT: (local $1 f64) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b6 (type $0) +;; CHECK-NEXT: (call $a6 +;; CHECK-NEXT: (call $get-i32) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a7 (type $4) (param $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b7 (type $0) +;; CHECK-NEXT: (call $a7 +;; CHECK-NEXT: (call $get-f64) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a8 (type $2) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b8 (type $0) +;; CHECK-NEXT: (call $a8 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a9 (type $2) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b9 (type $0) +;; CHECK-NEXT: (call $a9 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a10 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a11 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: ) + +;; CHECK: (func $a12 (type $2) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c1 (type $0) +;; CHECK-NEXT: (local $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $x +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c5 +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c6) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c7) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c8) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c2 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) + +;; CHECK: (func $c3 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) + +;; CHECK: (func $c4 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) + +;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) + +;; CHECK: (func $c6 (type $1) (result i32) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $c7 (type $1) (result i32) +;; CHECK-NEXT: (return +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c8 (type $1) (result i32) +;; CHECK-NEXT: (i32.const 5) +;; CHECK-NEXT: ) (module ;; indirect tail calls inhibit dropped result removal - ;; CHECK: (type $T (func (result i32))) (type $T (func (result i32))) (table 1 1 funcref) - ;; CHECK: (type $1 (func)) - - ;; CHECK: (table $0 1 1 funcref) - - ;; CHECK: (func $foo (type $T) (result i32) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (return_call_indirect $0 (type $T) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $foo (param $x i32) (result i32) (drop (return_call_indirect (type $T) @@ -452,11 +935,6 @@ ) ) ) - ;; CHECK: (func $bar (type $1) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $foo) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $bar (drop (call $foo @@ -465,86 +943,600 @@ ) ) ) -(module - ;; CHECK: (type $0 (func (param funcref i32 f64) (result i64))) +;; CHECK: (type $0 (func)) + +;; CHECK: (type $1 (func (param i32))) + +;; CHECK: (type $2 (func (result i32))) + +;; CHECK: (type $3 (func (result f64))) + +;; CHECK: (type $4 (func (param f64))) + +;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) + +;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) + +;; CHECK: (table $0 2 2 funcref) + +;; CHECK: (elem $0 (i32.const 0) $a9 $c8) + +;; CHECK: (export "a8" (func $a8)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: (call $a) +;; CHECK-NEXT: ) + +;; CHECK: (func $a1 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b1 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $b11 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $a2 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b2 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b22 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a3 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const -1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b3 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $b33 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $a4 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b4 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b43 (type $0) +;; CHECK-NEXT: (call $a4) +;; CHECK-NEXT: ) + +;; CHECK: (func $a5 (type $0) +;; CHECK-NEXT: (local $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b5 (type $0) +;; CHECK-NEXT: (call $a5) +;; CHECK-NEXT: ) + +;; CHECK: (func $a6 (type $1) (param $0 i32) +;; CHECK-NEXT: (local $1 f64) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b6 (type $0) +;; CHECK-NEXT: (call $a6 +;; CHECK-NEXT: (call $get-i32) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a7 (type $4) (param $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b7 (type $0) +;; CHECK-NEXT: (call $a7 +;; CHECK-NEXT: (call $get-f64) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a8 (type $1) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b8 (type $0) +;; CHECK-NEXT: (call $a8 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a9 (type $1) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b9 (type $0) +;; CHECK-NEXT: (call $a9 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a10 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a11 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: ) - ;; CHECK: (type $1 (func (param f32) (result funcref))) +;; CHECK: (func $a12 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) - ;; CHECK: (elem declare func $0) +;; CHECK: (func $c1 (type $0) +;; CHECK-NEXT: (local $x i32) +;; CHECK-NEXT: (call $c2) +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $x +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $c5 +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $c6) +;; CHECK-NEXT: (call $c7) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c8) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) - ;; CHECK: (export "export" (func $export)) +;; CHECK: (func $c2 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) - ;; CHECK: (func $0 (type $0) (param $0 funcref) (param $1 i32) (param $2 f64) (result i64) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) +;; CHECK: (func $c3 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c4 (type $2) (result i32) +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) + +;; CHECK: (func $c5 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c6 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $c7 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (return) +;; CHECK-NEXT: ) + +;; CHECK: (func $c8 (type $2) (result i32) +;; CHECK-NEXT: (i32.const 5) +;; CHECK-NEXT: ) +(module (func $0 (param $0 funcref) (param $1 i32) (param $2 f64) (result i64) (nop) (unreachable) ) - ;; CHECK: (func $export (type $1) (param $0 f32) (result funcref) - ;; CHECK-NEXT: (ref.func $0) - ;; CHECK-NEXT: ) (func $export (export "export") (param $0 f32) (result funcref) ;; a ref.func should prevent us from changing the type of a function, as it ;; may escape (ref.func $0) ) ) +;; CHECK: (type $0 (func)) + +;; CHECK: (type $1 (func (param i32))) + +;; CHECK: (type $2 (func (result i32))) + +;; CHECK: (type $3 (func (result f64))) + +;; CHECK: (type $4 (func (param f64))) + +;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) + +;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) + +;; CHECK: (table $0 2 2 funcref) + +;; CHECK: (elem $0 (i32.const 0) $a9 $c8) + +;; CHECK: (export "a8" (func $a8)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: (call $a) +;; CHECK-NEXT: ) + +;; CHECK: (func $a1 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b1 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $b11 (type $0) +;; CHECK-NEXT: (call $a1) +;; CHECK-NEXT: ) + +;; CHECK: (func $a2 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b2 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b22 (type $0) +;; CHECK-NEXT: (call $a2 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a3 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const -1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b3 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $b33 (type $0) +;; CHECK-NEXT: (call $a3) +;; CHECK-NEXT: ) + +;; CHECK: (func $a4 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b4 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $b43 (type $0) +;; CHECK-NEXT: (call $a4) +;; CHECK-NEXT: ) + +;; CHECK: (func $a5 (type $0) +;; CHECK-NEXT: (local $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b5 (type $0) +;; CHECK-NEXT: (call $a5) +;; CHECK-NEXT: ) + +;; CHECK: (func $a6 (type $1) (param $0 i32) +;; CHECK-NEXT: (local $1 f64) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (f64.const 3.14159) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b6 (type $0) +;; CHECK-NEXT: (call $a6 +;; CHECK-NEXT: (call $get-i32) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a7 (type $4) (param $0 f64) +;; CHECK-NEXT: (local $1 i32) +;; CHECK-NEXT: (local.set $1 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b7 (type $0) +;; CHECK-NEXT: (call $a7 +;; CHECK-NEXT: (call $get-f64) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a8 (type $1) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b8 (type $0) +;; CHECK-NEXT: (call $a8 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a9 (type $1) (param $x i32) +;; CHECK-NEXT: ) + +;; CHECK: (func $b9 (type $0) +;; CHECK-NEXT: (call $a9 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a10 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: (call $a10) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $a11 (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: (call $a11) +;; CHECK-NEXT: ) + +;; CHECK: (func $a12 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $a12 +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c1 (type $0) +;; CHECK-NEXT: (local $x i32) +;; CHECK-NEXT: (call $c2) +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: (call $c3) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.set $x +;; CHECK-NEXT: (call $c4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $c5 +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $c6) +;; CHECK-NEXT: (call $c7) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $c8) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c2 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c3 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c4 (type $2) (result i32) +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) + +;; CHECK: (func $c5 (type $1) (param $x i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $c6 (type $0) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) + +;; CHECK: (func $c7 (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (return) +;; CHECK-NEXT: ) + +;; CHECK: (func $c8 (type $2) (result i32) +;; CHECK-NEXT: (i32.const 5) +;; CHECK-NEXT: ) (module - ;; CHECK: (type $i64 (func (param i64))) (type $i64 (func (param i64))) - ;; CHECK: (type $1 (func)) - - ;; CHECK: (global $global$0 (ref $i64) (ref.func $0)) (global $global$0 (ref $i64) (ref.func $0)) - ;; CHECK: (export "even" (func $1)) (export "even" (func $1)) ;; the argument to this function cannot be removed due to the ref.func of it ;; in a global - ;; CHECK: (func $0 (type $i64) (param $0 i64) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) (func $0 (param $0 i64) (unreachable) ) - ;; CHECK: (func $1 (type $1) - ;; CHECK-NEXT: (call_ref $i64 - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $1 (call_ref $i64 (i64.const 0) (global.get $global$0) ) ) - ;; CHECK: (func $2 (type $1) - ;; CHECK-NEXT: (call $0 - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $2 (call $0 (i64.const 0) ) ) ) +;; CHECK: (type $0 (func)) + +;; CHECK: (type $1 (func (param i32) (result i32))) + +;; CHECK: (export "a" (func $a)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $b +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $1) (param $x i32) (result i32) +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) (module ;; a removable non-nullable parameter - ;; CHECK: (type $0 (func)) - - ;; CHECK: (func $0 (type $0) - ;; CHECK-NEXT: (local $0 i31ref) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) (func $0 (param $x i31ref) (nop) ) - ;; CHECK: (func $1 (type $0) - ;; CHECK-NEXT: (call $0) - ;; CHECK-NEXT: ) (func $1 (call $0 (ref.i31 (i32.const 0)) @@ -554,56 +1546,38 @@ ;; Arguments that read an immutable global can be optimized, as that is a ;; constant value. -(module - ;; CHECK: (type $0 (func)) +;; CHECK: (type $0 (func)) - ;; CHECK: (type $1 (func (param i32))) +;; CHECK: (type $1 (func (result i32))) - ;; CHECK: (type $2 (func (param i32 i32))) +;; CHECK: (export "a" (func $a)) - ;; CHECK: (global $immut i32 (i32.const 42)) +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $b) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $1) (result i32) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +(module (global $immut i32 (i32.const 42)) - ;; CHECK: (global $immut2 i32 (i32.const 43)) (global $immut2 i32 (i32.const 43)) - ;; CHECK: (global $mut (mut i32) (i32.const 1337)) (global $mut (mut i32) (i32.const 1337)) - ;; CHECK: (func $foo (type $1) (param $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (global.get $immut) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $foo (param $x i32) (param $y i32) ;; "Use" the params to avoid other optimizations kicking in. (drop (local.get $x)) (drop (local.get $y)) ) - ;; CHECK: (func $foo-caller (type $0) - ;; CHECK-NEXT: (global.set $mut - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $foo - ;; CHECK-NEXT: (global.get $mut) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $mut - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $foo - ;; CHECK-NEXT: (global.get $mut) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $foo-caller ;; Note how the mutable param has a different value in each call, which shows ;; the reason that we cannot optimize in this case. But we can optimize the @@ -620,35 +1594,11 @@ ) ) - ;; CHECK: (func $bar (type $2) (param $x i32) (param $y i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $bar (param $x i32) (param $y i32) (drop (local.get $x)) (drop (local.get $y)) ) - ;; CHECK: (func $bar-caller (type $0) - ;; CHECK-NEXT: (global.set $mut - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $bar - ;; CHECK-NEXT: (global.get $immut) - ;; CHECK-NEXT: (global.get $immut) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $mut - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $bar - ;; CHECK-NEXT: (global.get $mut) - ;; CHECK-NEXT: (global.get $immut2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $bar-caller ;; Corner cases of mixing mutable with immutable and mixing two immutables. (global.set $mut (i32.const 1)) @@ -664,28 +1614,26 @@ ) ) +;; CHECK: (type $0 (func)) + +;; CHECK: (export "a" (func $a)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (call $b) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (block (result i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) (module - ;; CHECK: (type $0 (func)) - - ;; CHECK: (func $0 (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $0 (param $0 i32) (result i32) ;; The returns here are nested in each other, and one is a recursive call to ;; this function itself, which makes this a corner case we might emit invalid @@ -703,9 +1651,6 @@ ) ) - ;; CHECK: (func $other-call (type $0) - ;; CHECK-NEXT: (call $0) - ;; CHECK-NEXT: ) (func $other-call (drop (call $0 @@ -715,20 +1660,28 @@ ) ) +;; CHECK: (type $0 (func)) + +;; CHECK: (export "a" (func $a)) + +;; CHECK: (func $a (type $0) +;; CHECK-NEXT: (call $b) +;; CHECK-NEXT: ) + +;; CHECK: (func $b (type $0) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (block (result i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) (module - ;; CHECK: (type $A (func (result (ref $A)))) (type $A (func (result (ref $A)))) - ;; CHECK: (type $1 (func)) - - ;; CHECK: (func $no-caller (type $A) (result (ref $A)) - ;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.null nofunc) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $no-caller (type $A) (result (ref $A)) ;; This return_call is to a bottom type, which we should ignore and not error ;; on. There is nothing to optimize here (other passes will turn this call @@ -739,11 +1692,6 @@ ) ) - ;; CHECK: (func $caller (type $1) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $no-caller) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $caller (drop (call $no-caller) @@ -751,28 +1699,21 @@ ) ) +;; CHECK: (type $0 (func (param i32) (result i32))) + +;; CHECK: (func $foo (type $0) (param $x i32) (result i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (return_call $bar +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $0) (param $x i32) (result i32) +;; CHECK-NEXT: (i32.const 7) +;; CHECK-NEXT: ) (module - ;; CHECK: (type $0 (func (param f64) (result i32))) - - ;; CHECK: (func $target (type $0) (param $0 f64) (result i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) - ;; CHECK-NEXT: (local $4 i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (f64.const 1.1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (f64.const 4.4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $target (param $a i32) (param $b f64) (param $c i32) (result i32) ;; Test removing a parameter despite calls having interesting non-unreachable ;; effects. This also tests recursion of such calls. We can remove all the i32 @@ -793,30 +1734,29 @@ ) ) -(module - ;; CHECK: (type $0 (func)) +;; CHECK: (type $0 (func (param i32) (result i32))) - ;; CHECK: (type $v128 (func (result v128))) - (type $v128 (func (result v128))) +;; CHECK: (type $1 (func (result i32))) - ;; CHECK: (type $2 (func (result f32))) +;; CHECK: (func $foo (type $0) (param $x i32) (result i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (return_call $bar) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $1) (result i32) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 7) +;; CHECK-NEXT: ) +(module + (type $v128 (func (result v128))) - ;; CHECK: (table $0 10 funcref) (table $0 10 funcref) - ;; CHECK: (func $caller-effects (type $0) - ;; CHECK-NEXT: (local $0 v128) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result f32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (call_indirect $0 (type $v128) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $target) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $caller-effects (drop (call $target @@ -831,54 +1771,31 @@ ) ) - ;; CHECK: (func $target (type $2) (result f32) - ;; CHECK-NEXT: (local $0 i64) - ;; CHECK-NEXT: (local $1 i64) - ;; CHECK-NEXT: (local $2 v128) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $target (param $0 i64) (param $1 v128) (param $2 i64) (result f32) ;; All parameters here should vanish. (unreachable) ) ) +;; CHECK: (type $0 (func (param i32) (result i32))) + +;; CHECK: (type $1 (func (result i32))) + +;; CHECK: (func $foo (type $0) (param $x i32) (result i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (return_call $bar) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $1) (result i32) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 7) +;; CHECK-NEXT: ) (module - ;; CHECK: (type $0 (func (param i32 i64))) - - ;; CHECK: (type $1 (func (param i64 i64))) - - ;; CHECK: (func $caller-later-br (type $0) (param $x i32) (param $y i64) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (block $block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $block) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $caller-later-br (param $x i32) (param $y i64) (block $block (drop @@ -915,20 +1832,6 @@ ) ) - ;; CHECK: (func $target (type $1) (param $0 i64) (param $1 i64) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result f32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $target (param $0 i64) (param $1 i32) (param $2 i64) (result f32) ;; The i32 parameter should vanish. (drop @@ -941,18 +1844,30 @@ ) ) -(module - ;; CHECK: (type $0 (func)) +;; CHECK: (type $0 (func (param i32) (result i32))) + +;; CHECK: (type $T (func (result i32))) + +;; CHECK: (type $2 (func)) + +;; CHECK: (table $0 1 1 funcref) - ;; CHECK: (type $1 (func (param i32))) +;; CHECK: (func $foo (type $0) (param $x i32) (result i32) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (return_call_indirect $0 (type $T) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) - ;; CHECK: (func $target (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) +;; CHECK: (func $bar (type $2) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $foo +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module (func $target (param $0 i32) ;; The parameter here is unused: there is a get, but it is unreachable. We can ;; remove the parameter here, and in the caller below. @@ -962,12 +1877,32 @@ ) ) - ;; CHECK: (func $caller (type $1) (param $x i32) - ;; CHECK-NEXT: (call $target) - ;; CHECK-NEXT: ) (func $caller (param $x i32) (call $target (local.get $x) ) ) ) +;; CHECK: (type $T (func (result i32))) + +;; CHECK: (type $1 (func)) + +;; CHECK: (table $0 1 1 funcref) + +;; CHECK: (func $foo (type $T) (result i32) +;; CHECK-NEXT: (local $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (return_call_indirect $0 (type $T) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar (type $1) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $foo) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae_tnh.wast b/test/lit/passes/dae_tnh.wast index 6e78283fdce..c7d0347a9f2 100644 --- a/test/lit/passes/dae_tnh.wast +++ b/test/lit/passes/dae_tnh.wast @@ -6,12 +6,11 @@ ;; CHECK: (type $struct (sub (struct (field i32)))) (type $struct (sub (struct (field i32)))) - ;; CHECK: (type $1 (func)) + ;; CHECK: (type $1 (func (param i32))) ;; CHECK: (type $2 (func (param (ref null $struct)))) - ;; CHECK: (func $target (type $1) - ;; CHECK-NEXT: (local $0 i32) + ;; CHECK: (func $target (type $1) (param $x i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $target (param $x i32) @@ -19,7 +18,11 @@ ) ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) - ;; CHECK-NEXT: (call $target) + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $caller (param $ref (ref null $struct)) (call $target @@ -34,12 +37,19 @@ ) (module - ;; CHECK: (type $0 (func)) + ;; CHECK: (type $struct (sub (struct (field i32)))) - ;; CHECK: (type $1 (func (param i32))) + ;; CHECK: (type $1 (func)) - ;; CHECK: (func $caller (type $0) - ;; CHECK-NEXT: (unreachable) + ;; CHECK: (type $2 (func (param (ref null $struct)))) + + ;; CHECK: (func $target (type $1) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + + ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) + ;; CHECK-NEXT: (call $target) ;; CHECK-NEXT: ) (func $caller ;; Removing this parameter would make the type of the call change from @@ -56,20 +66,25 @@ ) ) - ;; CHECK: (func $target (type $1) (param $0 i32) - ;; CHECK-NEXT: ) (func $target (param i32) ) ) ;; As above but the called target has a result. (module - ;; CHECK: (type $0 (func (result i32))) + ;; CHECK: (type $struct (sub (struct (field i32)))) - ;; CHECK: (type $1 (func (param i32) (result i32))) + ;; CHECK: (type $1 (func)) - ;; CHECK: (func $caller (type $0) (result i32) - ;; CHECK-NEXT: (unreachable) + ;; CHECK: (type $2 (func (param (ref null $struct)))) + + ;; CHECK: (func $target (type $1) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + + ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) + ;; CHECK-NEXT: (call $target) ;; CHECK-NEXT: ) (func $caller (result i32) ;; Again, the call is replaced by an unreachable. @@ -78,9 +93,6 @@ ) ) - ;; CHECK: (func $target (type $1) (param $0 i32) (result i32) - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) (func $target (param i32) (result i32) (i32.const 42) ) @@ -95,7 +107,9 @@ ;; CHECK: (type $1 (func (param i32))) ;; CHECK: (func $caller (type $0) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $caller (return_call $target @@ -110,18 +124,15 @@ ) (module - ;; CHECK: (type $0 (func (param i32))) + ;; CHECK: (type $0 (func)) - ;; CHECK: (type $1 (func)) + ;; CHECK: (type $1 (func (param i32))) - ;; CHECK: (func $target (type $0) (param $0 i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (f64.const 4.2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) + ;; CHECK: (func $caller (type $0) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + + ;; CHECK: (func $target (type $1) (param $0 i32) ;; CHECK-NEXT: ) (func $target (param $used i32) (param $unused f64) ;; One parameter is used, and one is not. @@ -130,11 +141,6 @@ ) ) - ;; CHECK: (func $caller (type $1) - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $caller ;; There is an unreachable parameter, and as in the cases above, we can't ;; remove it as it would change the type. But it isn't the param we want to From bb1ec77dfd2015c5e625745a8517e9e9b0dad301 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 16:44:50 -0800 Subject: [PATCH 10/44] waka? --- test/lit/passes/dae-gc-refine-params.wast | 97 +- test/lit/passes/dae-gc-refine-return.wast | 32 +- test/lit/passes/dae-gc.wast | 229 ++- test/lit/passes/dae-optimizing.wast | 140 +- .../dae-refine-params-and-optimize.wast | 19 +- test/lit/passes/dae_all-features.wast | 1733 ++++------------- test/lit/passes/dae_tnh.wast | 72 +- 7 files changed, 669 insertions(+), 1653 deletions(-) diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast index f627f116a67..27f9a214d11 100644 --- a/test/lit/passes/dae-gc-refine-params.wast +++ b/test/lit/passes/dae-gc-refine-params.wast @@ -19,7 +19,7 @@ (type $"{i32_f32}" (sub $"{i32}" (struct (field i32) (field f32)))) - ;; CHECK: (func $call-various-params-no (type $1) + ;; CHECK: (func $call-various-params-no (type $2) ;; CHECK-NEXT: (call $various-params-no ;; CHECK-NEXT: (call $"get_{}") ;; CHECK-NEXT: (call $"get_{i32}") @@ -45,7 +45,7 @@ ) ;; This function is called in ways that do not allow us to alter the types of ;; its parameters (see last function). - ;; CHECK: (func $various-params-no (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) + ;; CHECK: (func $various-params-no (type $8) (param $x (ref null $"{}")) (param $y (ref null $"{}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -59,26 +59,26 @@ (drop (local.get $y)) ) - ;; CHECK: (func $"get_{}" (type $10) (result (ref null $"{}")) + ;; CHECK: (func $"get_{}" (type $9) (result (ref null $"{}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{}" (result (ref null $"{}")) (unreachable) ) - ;; CHECK: (func $"get_{i32}" (type $6) (result (ref null $"{i32}")) + ;; CHECK: (func $"get_{i32}" (type $5) (result (ref null $"{i32}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{i32}" (result (ref null $"{i32}")) (unreachable) ) - ;; CHECK: (func $"get_{f64}" (type $11) (result (ref null $"{f64}")) + ;; CHECK: (func $"get_{f64}" (type $10) (result (ref null $"{f64}")) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $"get_{f64}" (result (ref null $"{f64}")) (unreachable) ) - ;; CHECK: (func $call-various-params-yes (type $1) + ;; CHECK: (func $call-various-params-yes (type $2) ;; CHECK-NEXT: (call $various-params-yes ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: (i32.const 0) @@ -107,7 +107,7 @@ ) ;; This function is called in ways that *do* allow us to alter the types of ;; its parameters (see last function). - ;; CHECK: (func $various-params-yes (type $12) (param $x (ref null $"{}")) (param $i i32) (param $y (ref null $"{}")) + ;; CHECK: (func $various-params-yes (type $11) (param $x (ref null $"{i32}")) (param $i i32) (param $y (ref null $"{i32}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -125,7 +125,7 @@ (drop (local.get $y)) ) - ;; CHECK: (func $call-various-params-set (type $1) + ;; CHECK: (func $call-various-params-set (type $2) ;; CHECK-NEXT: (call $various-params-set ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: (call $"get_null_{i32}") @@ -150,24 +150,30 @@ ;; This function is called in ways that *do* allow us to alter the types of ;; its parameters (see last function), however, we reuse the parameters by ;; writing to them, which causes problems in one case. - ;; CHECK: (func $various-params-set (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) - ;; CHECK-NEXT: (drop + ;; CHECK: (func $various-params-set (type $12) (param $x (ref null $"{i32}")) (param $y (ref null $"{i32}")) + ;; CHECK-NEXT: (local $2 (ref null $"{}")) + ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (struct.new_default $"{}") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $y - ;; CHECK-NEXT: (call $"get_null_{i32_i64}") - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (struct.new_default $"{}") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $y + ;; CHECK-NEXT: (call $"get_null_{i32_i64}") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $various-params-set (param $x (ref null $"{}")) (param $y (ref null $"{}")) @@ -190,7 +196,7 @@ ) ) - ;; CHECK: (func $call-various-params-tee (type $1) + ;; CHECK: (func $call-various-params-tee (type $2) ;; CHECK-NEXT: (call $various-params-tee ;; CHECK-NEXT: (call $"get_null_{i32}") ;; CHECK-NEXT: ) @@ -201,12 +207,12 @@ (call $"get_null_{i32}") ) ) - ;; CHECK: (func $various-params-tee (type $7) (param $x (ref null $"{}")) + ;; CHECK: (func $various-params-tee (type $6) (param $x (ref null $"{i32}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref null $"{}")) + ;; CHECK-NEXT: (block (result (ref null $"{i32}")) ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (call $"get_null_{i32_i64}") ;; CHECK-NEXT: ) @@ -226,7 +232,7 @@ ) ) - ;; CHECK: (func $call-various-params-null (type $1) + ;; CHECK: (func $call-various-params-null (type $2) ;; CHECK-NEXT: (call $various-params-null ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (ref.null none) @@ -256,7 +262,7 @@ ) ;; This function is called in ways that allow us to make the first parameter ;; non-nullable. - ;; CHECK: (func $various-params-null (type $5) (param $x (ref null $"{}")) (param $y (ref null $"{}")) + ;; CHECK: (func $various-params-null (type $13) (param $x (ref none)) (param $y (ref null $"{i32}")) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) @@ -280,7 +286,7 @@ (local.set $temp (local.get $temp)) ) - ;; CHECK: (func $call-various-params-middle (type $1) + ;; CHECK: (func $call-various-params-middle (type $2) ;; CHECK-NEXT: (call $various-params-middle ;; CHECK-NEXT: (call $"get_null_{i32_i64}") ;; CHECK-NEXT: ) @@ -298,7 +304,7 @@ (call $"get_null_{i32_f32}") ) ) - ;; CHECK: (func $various-params-middle (type $7) (param $x (ref null $"{}")) + ;; CHECK: (func $various-params-middle (type $6) (param $x (ref null $"{i32}")) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -308,7 +314,8 @@ (drop (local.get $x)) ) - ;; CHECK: (func $unused-and-refinable (type $8) (param $0 structref) + ;; CHECK: (func $unused-and-refinable (type $2) + ;; CHECK-NEXT: (local $0 structref) ;; CHECK-NEXT: ) (func $unused-and-refinable (param $0 structref) ;; This function does not use $0. It is called with $"{}", so it is also @@ -322,10 +329,8 @@ ;; local). ) - ;; CHECK: (func $call-unused-and-refinable (type $1) - ;; CHECK-NEXT: (call $unused-and-refinable - ;; CHECK-NEXT: (struct.new_default $"{}") - ;; CHECK-NEXT: ) + ;; CHECK: (func $call-unused-and-refinable (type $2) + ;; CHECK-NEXT: (call $unused-and-refinable) ;; CHECK-NEXT: ) (func $call-unused-and-refinable (call $unused-and-refinable @@ -333,10 +338,14 @@ ) ) - ;; CHECK: (func $non-nullable-fixup (type $8) (param $0 structref) - ;; CHECK-NEXT: (local.set $0 + ;; CHECK: (func $non-nullable-fixup (type $14) (param $0 (ref (exact $"{}"))) + ;; CHECK-NEXT: (local $1 structref) + ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $non-nullable-fixup (param $0 structref) ;; Use the param to avoid other opts removing it, and to force us to do a @@ -347,7 +356,7 @@ ) ) - ;; CHECK: (func $call-non-nullable-fixup (type $1) + ;; CHECK: (func $call-non-nullable-fixup (type $2) ;; CHECK-NEXT: (call $non-nullable-fixup ;; CHECK-NEXT: (struct.new_default $"{}") ;; CHECK-NEXT: ) @@ -358,7 +367,7 @@ ) ) - ;; CHECK: (func $call-update-null (type $1) + ;; CHECK: (func $call-update-null (type $2) ;; CHECK-NEXT: (call $update-null ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -377,7 +386,7 @@ ) ) - ;; CHECK: (func $update-null (type $13) (param $x anyref) + ;; CHECK: (func $update-null (type $15) (param $x (ref null (exact $"{}"))) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -389,7 +398,7 @@ (drop (local.get $x)) ) - ;; CHECK: (func $"get_null_{i32}" (type $6) (result (ref null $"{i32}")) + ;; CHECK: (func $"get_null_{i32}" (type $5) (result (ref null $"{i32}")) ;; CHECK-NEXT: (select (result (ref null $"{i32}")) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (select (result (ref $"{i32}")) @@ -416,7 +425,7 @@ ) ) - ;; CHECK: (func $"get_null_{i32_i64}" (type $14) (result (ref null $"{i32_i64}")) + ;; CHECK: (func $"get_null_{i32_i64}" (type $16) (result (ref null (exact $"{i32_i64}"))) ;; CHECK-NEXT: (select (result (ref null (exact $"{i32_i64}"))) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (struct.new_default $"{i32_i64}") @@ -431,7 +440,7 @@ ) ) - ;; CHECK: (func $"get_null_{i32_f32}" (type $15) (result (ref null $"{i32_f32}")) + ;; CHECK: (func $"get_null_{i32_f32}" (type $17) (result (ref null (exact $"{i32_f32}"))) ;; CHECK-NEXT: (select (result (ref null (exact $"{i32_f32}"))) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (struct.new_default $"{i32_f32}") diff --git a/test/lit/passes/dae-gc-refine-return.wast b/test/lit/passes/dae-gc-refine-return.wast index bd3aeef5cc9..1ef7bc1b13c 100644 --- a/test/lit/passes/dae-gc-refine-return.wast +++ b/test/lit/passes/dae-gc-refine-return.wast @@ -55,7 +55,7 @@ ) ;; Refine the return type based on the value flowing out. - ;; CHECK: (func $refine-return-flow (type $0) (result anyref) + ;; CHECK: (func $refine-return-flow (type $4) (result i31ref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -71,12 +71,12 @@ (local.get $i31) ) - ;; CHECK: (func $call-refine-return-flow (type $0) (result anyref) + ;; CHECK: (func $call-refine-return-flow (type $4) (result i31ref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local.set $temp ;; CHECK-NEXT: (call $call-refine-return-flow) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if (result anyref) + ;; CHECK-NEXT: (if (result i31ref) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (call $refine-return-flow) @@ -105,7 +105,7 @@ ) ;; Refine the return type based on a return. - ;; CHECK: (func $refine-return-return (type $0) (result anyref) + ;; CHECK: (func $refine-return-return (type $4) (result i31ref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -125,7 +125,7 @@ ) ;; Refine the return type based on multiple values. - ;; CHECK: (func $refine-return-many (type $0) (result anyref) + ;; CHECK: (func $refine-return-many (type $4) (result i31ref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -170,7 +170,7 @@ (local.get $i31) ) - ;; CHECK: (func $refine-return-many-lub (type $0) (result anyref) + ;; CHECK: (func $refine-return-many-lub (type $7) (result eqref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local $struct structref) @@ -218,7 +218,7 @@ (local.get $i31) ) - ;; CHECK: (func $refine-return-many-lub-2 (type $0) (result anyref) + ;; CHECK: (func $refine-return-many-lub-2 (type $7) (result eqref) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local $struct structref) @@ -267,7 +267,7 @@ ) ;; We can refine the return types of tuples. - ;; CHECK: (func $refine-return-tuple (type $5) (result anyref i32) + ;; CHECK: (func $refine-return-tuple (type $8) (result i31ref i32) ;; CHECK-NEXT: (local $temp anyref) ;; CHECK-NEXT: (local $i31 i31ref) ;; CHECK-NEXT: (local.set $temp @@ -306,7 +306,7 @@ (func $do-return-call (result funcref) (return_call $return-ref-func) ) - ;; CHECK: (func $return-ref-func (type $6) (result funcref) + ;; CHECK: (func $return-ref-func (type $9) (result (ref (exact $6))) ;; CHECK-NEXT: (ref.func $do-return-call) ;; CHECK-NEXT: ) (func $return-ref-func (result funcref) @@ -321,7 +321,7 @@ (func $tail-callee (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-yes (type $0) (result anyref) + ;; CHECK: (func $tail-caller-yes (type $"return_{}") (result (ref $"{}")) ;; CHECK-NEXT: (return_call $tail-callee) ;; CHECK-NEXT: ) (func $tail-caller-yes (result anyref) @@ -353,7 +353,7 @@ ) (return_call $tail-callee) ) - ;; CHECK: (func $tail-call-caller (type $3) + ;; CHECK: (func $tail-call-caller (type $5) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-yes) ;; CHECK-NEXT: ) @@ -378,7 +378,7 @@ (func $tail-callee-indirect (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-indirect-yes (type $0) (result anyref) + ;; CHECK: (func $tail-caller-indirect-yes (type $"return_{}") (result (ref $"{}")) ;; CHECK-NEXT: (return_call_indirect $0 (type $"return_{}") ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -410,7 +410,7 @@ ) (return_call_indirect (type $"return_{}") (i32.const 0)) ) - ;; CHECK: (func $tail-call-caller-indirect (type $3) + ;; CHECK: (func $tail-call-caller-indirect (type $5) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-indirect-yes) ;; CHECK-NEXT: ) @@ -434,7 +434,7 @@ (func $tail-callee-call_ref (result (ref $"{}")) (unreachable) ) - ;; CHECK: (func $tail-caller-call_ref-yes (type $0) (result anyref) + ;; CHECK: (func $tail-caller-call_ref-yes (type $"return_{}") (result (ref $"{}")) ;; CHECK-NEXT: (local $"return_{}" (ref null $"return_{}")) ;; CHECK-NEXT: (return_call_ref $"return_{}" ;; CHECK-NEXT: (local.get $"return_{}") @@ -484,7 +484,7 @@ ;; should not hit an assertion on such things. (return_call_ref $"return_{}" (unreachable)) ) - ;; CHECK: (func $tail-call-caller-call_ref (type $3) + ;; CHECK: (func $tail-call-caller-call_ref (type $5) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $tail-caller-call_ref-yes) ;; CHECK-NEXT: ) @@ -507,7 +507,7 @@ ) ) - ;; CHECK: (func $update-null (type $7) (param $x i32) (param $y i32) (result anyref) + ;; CHECK: (func $update-null (type $10) (param $x i32) (param $y i32) (result (ref null $"{i32}")) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (then diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 4f0714ce9dd..2a9fa99cd5f 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -2,15 +2,13 @@ ;; RUN: foreach %s %t wasm-opt -all --dae -S -o - | filecheck %s (module + ;; CHECK: (type $0 (func)) + ;; CHECK: (type $"{}" (struct)) (type $"{}" (struct)) - ;; CHECK: (func $foo (type $1) - ;; CHECK-NEXT: (call $bar - ;; CHECK-NEXT: (ref.i31 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK: (func $foo (type $0) + ;; CHECK-NEXT: (call $bar) ;; CHECK-NEXT: ) (func $foo (call $bar @@ -19,7 +17,8 @@ ) ) ) - ;; CHECK: (func $bar (type $2) (param $0 i31ref) + ;; CHECK: (func $bar (type $0) + ;; CHECK-NEXT: (local $0 i31ref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $0 ;; CHECK-NEXT: (ref.i31 @@ -52,16 +51,15 @@ ) ;; A function that gets a non-nullable reference that is never used. We can ;; still create a non-nullable local for that parameter. - ;; CHECK: (func $get-nonnull (type $3) (param $0 (ref $"{}")) + ;; CHECK: (func $get-nonnull (type $0) + ;; CHECK-NEXT: (local $0 (ref $"{}")) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $get-nonnull (param $0 (ref $"{}")) (nop) ) - ;; CHECK: (func $send-nonnull (type $1) - ;; CHECK-NEXT: (call $get-nonnull - ;; CHECK-NEXT: (struct.new_default $"{}") - ;; CHECK-NEXT: ) + ;; CHECK: (func $send-nonnull (type $0) + ;; CHECK-NEXT: (call $get-nonnull) ;; CHECK-NEXT: ) (func $send-nonnull (call $get-nonnull @@ -74,10 +72,19 @@ (module ;; CHECK: (type $0 (func)) - ;; CHECK: (type $"{}" (struct)) - - ;; CHECK: (func $foo (type $0) - ;; CHECK-NEXT: (call $bar) + ;; CHECK: (func $foo (type $1) (param $0 (ref (exact $0))) + ;; CHECK-NEXT: (local $1 (ref (exact $0))) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $foo (param $x (ref func)) (param $y (ref func)) ;; "Use" the params to avoid other optimizations kicking in. @@ -85,6 +92,14 @@ (drop (local.get $y)) ) + ;; CHECK: (func $call-foo (type $0) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (ref.func $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $call-foo ;; Call $foo with a constant function in the first param, which we ;; can optimize, but different ones in the second. @@ -98,17 +113,18 @@ ) ) - ;; CHECK: (func $bar (type $0) - ;; CHECK-NEXT: (local $0 i31ref) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (ref.i31 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK: (func $bar (type $2) (param $0 i31ref) + ;; CHECK-NEXT: (local $1 nullref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $bar (param $x (ref null any)) (param $y (ref null any)) @@ -117,6 +133,16 @@ (drop (local.get $y)) ) + ;; CHECK: (func $call-bar (type $0) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (ref.i31 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $call-bar ;; Call with nulls. Mixing nulls is fine as they all have the same type and ;; value. However, mixing a null with a reference stops us in the second @@ -132,27 +158,43 @@ ) ;; Helper functions so we have something to take the reference of. + ;; CHECK: (func $a (type $0) + ;; CHECK-NEXT: ) (func $a) + ;; CHECK: (func $b (type $0) + ;; CHECK-NEXT: ) (func $b) + ;; CHECK: (func $c (type $0) + ;; CHECK-NEXT: ) (func $c) ) ;; Test that string constants can be applied. -;; CHECK: (func $get-nonnull (type $0) -;; CHECK-NEXT: (local $0 (ref $"{}")) -;; CHECK-NEXT: (nop) -;; CHECK-NEXT: ) - -;; CHECK: (func $send-nonnull (type $0) -;; CHECK-NEXT: (call $get-nonnull) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $0 (type $0) + ;; CHECK-NEXT: (call $1) + ;; CHECK-NEXT: ) (func $0 (call $1 (string.const "310") (string.const "929") ) ) + ;; CHECK: (func $1 (type $0) + ;; CHECK-NEXT: (local $0 (ref string)) + ;; CHECK-NEXT: (local $1 (ref string)) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (string.const "929") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (string.const "310") + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $1 (param $0 (ref string)) (param $1 (ref string)) ;; The parameters here will be removed, and the constant values placed in the ;; function. @@ -165,49 +207,43 @@ ;; $1's caller $2, and note that $2 also calls $6, so we must update some of ;; $6's callers but not all. ;; TODO: pretty names etc. -;; CHECK: (type $0 (func)) - -;; CHECK: (type $"{}" (struct)) - -;; CHECK: (func $foo (type $0) -;; CHECK-NEXT: (call $bar) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $0) -;; CHECK-NEXT: (local $0 i31ref) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.tee $0 -;; CHECK-NEXT: (ref.i31 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.tee $0 -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $get-nonnull (type $0) -;; CHECK-NEXT: (local $0 (ref $"{}")) -;; CHECK-NEXT: (nop) -;; CHECK-NEXT: ) - -;; CHECK: (func $send-nonnull (type $0) -;; CHECK-NEXT: (call $get-nonnull) -;; CHECK-NEXT: ) (module (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $S (struct)) (type $S (struct)) - ;; CHECK: (type $0 (func)) + ;; CHECK: (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) ) + ;; CHECK: (func $0 (type $5) (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $0 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) (unreachable) ) + ;; CHECK: (func $1 (type $4) (result f64) + ;; CHECK-NEXT: (local $0 (ref struct)) + ;; CHECK-NEXT: (local $1 (ref $0)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $1 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) (unreachable) ) + ;; CHECK: (func $2 (type $1) + ;; CHECK-NEXT: (local $0 (ref (exact $0))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result f64) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (struct.new $0 + ;; CHECK-NEXT: (call $6) + ;; CHECK-NEXT: (ref.func $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $2 (drop (call $1 @@ -219,9 +255,19 @@ ) ) ) + ;; CHECK: (func $4 (type $1) + ;; CHECK-NEXT: (local $0 (ref any)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $4 (param $0 (ref any)) (result f64) (unreachable) ) + ;; CHECK: (func $5 (type $1) + ;; CHECK-NEXT: (call $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $5 (drop (call $4 @@ -235,58 +281,11 @@ (call $6) ) ) + ;; CHECK: (func $6 (type $4) (result f64) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $6 (result f64) (unreachable) ) ) -;; CHECK: (func $foo (type $1) (param $x (ref func)) (param $y (ref func)) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $y) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $call-foo (type $0) -;; CHECK-NEXT: (call $foo -;; CHECK-NEXT: (ref.func $a) -;; CHECK-NEXT: (ref.func $b) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $foo -;; CHECK-NEXT: (ref.func $a) -;; CHECK-NEXT: (ref.func $c) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $2) (param $x anyref) (param $y anyref) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $y) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $call-bar (type $0) -;; CHECK-NEXT: (call $bar -;; CHECK-NEXT: (ref.null none) -;; CHECK-NEXT: (ref.null none) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $bar -;; CHECK-NEXT: (ref.null none) -;; CHECK-NEXT: (ref.i31 -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: ) - -;; CHECK: (func $c (type $0) -;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae-optimizing.wast b/test/lit/passes/dae-optimizing.wast index d61f2d51152..316c377586f 100644 --- a/test/lit/passes/dae-optimizing.wast +++ b/test/lit/passes/dae-optimizing.wast @@ -4,55 +4,48 @@ ;; RUN: foreach %s %t wasm-opt -all --dae-optimizing -S -o - | filecheck %s (module - ;; CHECK: (type $1 (func (param f64 f32 f32 f64 f32 i64 f64) (result i32))) - - ;; CHECK: (type $0 (func (param f32) (result f32))) (type $0 (func (param f32) (result f32))) (type $1 (func (param f64 f32 f32 f64 f32 i64 f64) (result i32))) + ;; CHECK: (type $3 (func (result i32))) + + ;; CHECK: (type $4 (func (result f32))) + ;; CHECK: (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) ;; CHECK: (global $global$0 (mut i32) (i32.const 10)) (global $global$0 (mut i32) (i32.const 10)) - ;; CHECK: (func $0 (type $1) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i64) (param $6 f64) (result i32) - ;; CHECK-NEXT: (local $7 i32) - ;; CHECK-NEXT: (local $8 i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.tee $7 - ;; CHECK-NEXT: (i32.const 33554432) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (drop + ;; CHECK: (func $0 (type $3) (result i32) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (if (result f32) + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (i32.const 33554432) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then ;; CHECK-NEXT: (loop $label$2 (result f32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (global.get $global$0) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (local.get $7) + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $8 - ;; CHECK-NEXT: (block $label$4 (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.tee $7 - ;; CHECK-NEXT: (local.get $8) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br_if $label$2 - ;; CHECK-NEXT: (local.get $7) + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (f32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $1 - ;; CHECK-NEXT: (f32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (call $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -103,22 +96,14 @@ ) (i32.const -11) ) - ;; CHECK: (func $1 (type $0) (param $0 f32) (result f32) + ;; CHECK: (func $1 (type $4) (result f32) ;; CHECK-NEXT: (f32.const 0) ;; CHECK-NEXT: ) (func $1 (; 1 ;) (type $0) (param $0 f32) (result f32) (f32.const 0) ) ;; CHECK: (func $2 (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) - ;; CHECK-NEXT: (call $0 - ;; CHECK-NEXT: (f64.const 1) - ;; CHECK-NEXT: (f32.const 1) - ;; CHECK-NEXT: (f32.const 1) - ;; CHECK-NEXT: (f64.const 1) - ;; CHECK-NEXT: (f32.const 1) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: (f64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $0) ;; CHECK-NEXT: ) (func $2 (; 2 ;) (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) (call $0 @@ -137,6 +122,22 @@ (module (func $import (import "" "") (result i32)) (func $impossible (import "" "") (result (ref none))) + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (type $1 (func (result (ref none)))) + + ;; CHECK: (type $2 (func)) + + ;; CHECK: (import "" "" (func $import (type $0) (result i32))) + + ;; CHECK: (import "" "" (func $impossible (type $1) (result (ref none)))) + + ;; CHECK: (export "export" (func $export)) + + ;; CHECK: (func $export (type $0) (result i32) + ;; CHECK-NEXT: (call $internal) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $export (export "export") (result i32) (drop ;; This should be optimized to an unreachable sequence. @@ -152,6 +153,14 @@ ) (i32.const 0) ) + ;; CHECK: (func $internal (type $2) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $impossible) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $internal (param i32) (result (ref none)) ;; Prevent this from being removed entirely. (drop @@ -160,56 +169,3 @@ (call $impossible) ) ) -;; CHECK: (type $3 (func (result i32))) - -;; CHECK: (type $4 (func (result f32))) - -;; CHECK: (type $2 (func (param f64 f32 f32 f64 f32 i32 i32 f64) (result i32))) - -;; CHECK: (global $global$0 (mut i32) (i32.const 10)) - -;; CHECK: (func $0 (type $3) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (if (result f32) -;; CHECK-NEXT: (local.tee $0 -;; CHECK-NEXT: (i32.const 33554432) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (loop $label$2 (result f32) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (global.get $global$0) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (return -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (br_if $label$2 -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (f32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (else -;; CHECK-NEXT: (call $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const -11) -;; CHECK-NEXT: ) - -;; CHECK: (func $1 (type $4) (result f32) -;; CHECK-NEXT: (f32.const 0) -;; CHECK-NEXT: ) - -;; CHECK: (func $2 (type $2) (param $0 f64) (param $1 f32) (param $2 f32) (param $3 f64) (param $4 f32) (param $5 i32) (param $6 i32) (param $7 f64) (result i32) -;; CHECK-NEXT: (call $0) -;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae-refine-params-and-optimize.wast b/test/lit/passes/dae-refine-params-and-optimize.wast index 766af22d38e..9d893c21e54 100644 --- a/test/lit/passes/dae-refine-params-and-optimize.wast +++ b/test/lit/passes/dae-refine-params-and-optimize.wast @@ -4,23 +4,16 @@ ;; The br_on_cast_fail is optimized away thanks to the refined type. The output still has some unoptimized code (an additional --dce pass would get rid of the drop-return pattern here), but that is not directly relevant to this test. (module - ;; CHECK: (type $0 (func (param (ref eq)) (result i32))) + ;; CHECK: (type $0 (func (param (ref array)) (result i32))) - ;; CHECK: (type $1 (func (param (ref array)) (result i32))) - - ;; CHECK: (func $len (type $0) (param $0 (ref eq)) (result i32) + ;; CHECK: (func $len (type $0) (param $0 (ref array)) (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $not_array (result (ref eq)) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (array.len - ;; CHECK-NEXT: (br_on_cast_fail $not_array (ref eq) (ref array) - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (array.len + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: ) (func $len (param (ref eq)) (result i32) (drop @@ -36,7 +29,7 @@ ) (i32.const -1) ) - ;; CHECK: (func $optimize-after-refinement (type $1) (param $0 (ref array)) (result i32) + ;; CHECK: (func $optimize-after-refinement (type $0) (param $0 (ref array)) (result i32) ;; CHECK-NEXT: (call $len ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae_all-features.wast b/test/lit/passes/dae_all-features.wast index 2158d7c23e7..145ae1ff032 100644 --- a/test/lit/passes/dae_all-features.wast +++ b/test/lit/passes/dae_all-features.wast @@ -11,15 +11,13 @@ ;; CHECK: (type $2 (func (result i32))) - ;; CHECK: (type $3 (func (param i32 f64))) - - ;; CHECK: (type $4 (func (result f64))) + ;; CHECK: (type $3 (func (result f64))) - ;; CHECK: (type $5 (func (param i32) (result i32))) + ;; CHECK: (type $4 (func (param f64))) ;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) (import "a" "b" (func $get-i32 (result i32))) - ;; CHECK: (import "a" "c" (func $get-f64 (type $4) (result f64))) + ;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) (import "a" "c" (func $get-f64 (result f64))) ;; CHECK: (table $0 2 2 funcref) @@ -30,35 +28,39 @@ (export "a8" (func $a8)) (table 2 2 funcref) (elem (i32.const 0) $a9 $c8) - ;; CHECK: (func $a (type $1) (param $x i32) + ;; CHECK: (func $a (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a (param $x i32)) ;; CHECK: (func $b (type $0) - ;; CHECK-NEXT: (call $a - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a) ;; CHECK-NEXT: ) (func $b (call $a (i32.const 1)) ;; best case scenario ) - ;; CHECK: (func $a1 (type $1) (param $x i32) + ;; CHECK: (func $a1 (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $a1 (param $x i32) (unreachable) ) ;; CHECK: (func $b1 (type $0) - ;; CHECK-NEXT: (call $a1 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a1) ;; CHECK-NEXT: ) (func $b1 (call $a1 (i32.const 2)) ;; same value in both, so works ) ;; CHECK: (func $b11 (type $0) - ;; CHECK-NEXT: (call $a1 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a1) ;; CHECK-NEXT: ) (func $b11 (call $a1 (i32.const 2)) @@ -87,7 +89,8 @@ (func $b22 (call $a2 (i32.const 4)) ) - ;; CHECK: (func $a3 (type $1) (param $x i32) + ;; CHECK: (func $a3 (type $0) + ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: ) @@ -96,31 +99,31 @@ (drop (i32.const -1)) ;; diff value, but at least unused, so no need to send ) ;; CHECK: (func $b3 (type $0) - ;; CHECK-NEXT: (call $a3 - ;; CHECK-NEXT: (i32.const 3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a3) ;; CHECK-NEXT: ) (func $b3 (call $a3 (i32.const 3)) ) ;; CHECK: (func $b33 (type $0) - ;; CHECK-NEXT: (call $a3 - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a3) ;; CHECK-NEXT: ) (func $b33 (call $a3 (i32.const 4)) ) - ;; CHECK: (func $a4 (type $1) (param $x i32) + ;; CHECK: (func $a4 (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a4 (param $x i32) ;; This function is called with one constant and one unreachable. We can ;; remove the param despite the unreachable's effects. ) ;; CHECK: (func $b4 (type $0) - ;; CHECK-NEXT: (call $a4 - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $b4 ;; This call will vanish entirely, because the unreachable child executes @@ -129,20 +132,30 @@ (call $a4 (unreachable)) ) ;; CHECK: (func $b43 (type $0) - ;; CHECK-NEXT: (call $a4 - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a4) ;; CHECK-NEXT: ) (func $b43 ;; We will remove the parameter here. (call $a4 (i32.const 4)) ) - ;; CHECK: (func $a5 (type $3) (param $x i32) (param $y f64) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) + ;; CHECK: (func $a5 (type $0) + ;; CHECK-NEXT: (local $0 f64) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (f64.const 3.14159) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a5 (param $x i32) (param $y f64) ;; optimize two @@ -150,20 +163,23 @@ (drop (local.get $y)) ) ;; CHECK: (func $b5 (type $0) - ;; CHECK-NEXT: (call $a5 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: (f64.const 3.14159) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $a5) ;; CHECK-NEXT: ) (func $b5 (call $a5 (i32.const 1) (f64.const 3.14159)) ) - ;; CHECK: (func $a6 (type $3) (param $x i32) (param $y f64) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) + ;; CHECK: (func $a6 (type $1) (param $0 i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (f64.const 3.14159) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a6 (param $x i32) (param $y f64) ;; optimize just one @@ -173,18 +189,23 @@ ;; CHECK: (func $b6 (type $0) ;; CHECK-NEXT: (call $a6 ;; CHECK-NEXT: (call $get-i32) - ;; CHECK-NEXT: (f64.const 3.14159) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b6 (call $a6 (call $get-i32) (f64.const 3.14159)) ) - ;; CHECK: (func $a7 (type $3) (param $x i32) (param $y f64) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) + ;; CHECK: (func $a7 (type $4) (param $0 f64) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a7 (param $x i32) (param $y f64) ;; optimize just the other one @@ -193,7 +214,6 @@ ) ;; CHECK: (func $b7 (type $0) ;; CHECK-NEXT: (call $a7 - ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (call $get-f64) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -222,25 +242,24 @@ (func $b9 (call $a9 (i32.const 1)) ) - ;; CHECK: (func $a10 (type $1) (param $x i32) - ;; CHECK-NEXT: (call $a10 + ;; CHECK: (func $a10 (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $a10 - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $a10) + ;; CHECK-NEXT: (call $a10) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $a10 (param $x i32) ;; recursion (call $a10 (i32.const 1)) (call $a10 (i32.const 1)) ) - ;; CHECK: (func $a11 (type $1) (param $x i32) - ;; CHECK-NEXT: (call $a11 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $a11 - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK: (func $a11 (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (call $a11) + ;; CHECK-NEXT: (call $a11) ;; CHECK-NEXT: ) (func $a11 (param $x i32) ;; partially successful recursion (call $a11 (i32.const 1)) @@ -265,32 +284,20 @@ ;; return values ;; CHECK: (func $c1 (type $0) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $c2) + ;; CHECK-NEXT: (call $c3) + ;; CHECK-NEXT: (call $c3) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $c4) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (call $c4) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c5 - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c6) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $c7) + ;; CHECK-NEXT: (call $c5 + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $c6) + ;; CHECK-NEXT: (call $c7) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $c8) ;; CHECK-NEXT: ) @@ -307,14 +314,18 @@ (drop (call $c7)) (drop (call $c8)) ) - ;; CHECK: (func $c2 (type $2) (result i32) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK: (func $c2 (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $c2 (result i32) (i32.const 1) ) - ;; CHECK: (func $c3 (type $2) (result i32) - ;; CHECK-NEXT: (i32.const 2) + ;; CHECK: (func $c3 (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $c3 (result i32) (i32.const 2) @@ -325,22 +336,25 @@ (func $c4 (result i32) (i32.const 3) ) - ;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) - ;; CHECK-NEXT: (local.get $x) + ;; CHECK: (func $c5 (type $1) (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $c5 (param $x i32) (result i32) (local.get $x) ) - ;; CHECK: (func $c6 (type $2) (result i32) + ;; CHECK: (func $c6 (type $0) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $c6 (result i32) (unreachable) ) - ;; CHECK: (func $c7 (type $2) (result i32) - ;; CHECK-NEXT: (return + ;; CHECK: (func $c7 (type $0) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 4) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) (func $c7 (result i32) (return (i32.const 4)) @@ -355,33 +369,10 @@ (module ;; both operations at once: remove params and return value ;; CHECK: (type $0 (func)) - ;; CHECK: (type $1 (func (result i32))) - - ;; CHECK: (type $2 (func (param i32))) - - ;; CHECK: (type $3 (func (result f64))) - - ;; CHECK: (type $4 (func (param f64))) - - ;; CHECK: (type $5 (func (param i32) (result i32))) - - ;; CHECK: (import "a" "b" (func $get-i32 (type $1) (result i32))) - - ;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) - - ;; CHECK: (table $0 2 2 funcref) - - ;; CHECK: (elem $0 (i32.const 0) $a9 $c8) - - ;; CHECK: (export "a8" (func $a8)) + ;; CHECK: (export "a" (func $a)) ;; CHECK: (func $a (type $0) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $b) ;; CHECK-NEXT: ) (func $a (export "a") (drop @@ -391,251 +382,31 @@ ) ) ;; CHECK: (func $b (type $0) - ;; CHECK-NEXT: (call $a) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $b (param $x i32) (result i32) (local.get $x) ) ) -;; CHECK: (func $a1 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b1 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $b11 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $a2 (type $2) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b2 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b22 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a3 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const -1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b3 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $b33 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $a4 (type $2) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b4 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b43 (type $0) -;; CHECK-NEXT: (call $a4 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a5 (type $0) -;; CHECK-NEXT: (local $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b5 (type $0) -;; CHECK-NEXT: (call $a5) -;; CHECK-NEXT: ) - -;; CHECK: (func $a6 (type $2) (param $0 i32) -;; CHECK-NEXT: (local $1 f64) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b6 (type $0) -;; CHECK-NEXT: (call $a6 -;; CHECK-NEXT: (call $get-i32) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a7 (type $4) (param $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b7 (type $0) -;; CHECK-NEXT: (call $a7 -;; CHECK-NEXT: (call $get-f64) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a8 (type $2) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b8 (type $0) -;; CHECK-NEXT: (call $a8 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a9 (type $2) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b9 (type $0) -;; CHECK-NEXT: (call $a9 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a10 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a11 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: ) - -;; CHECK: (func $a12 (type $2) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c1 (type $0) -;; CHECK-NEXT: (local $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $x -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c5 -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c6) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c7) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c8) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c2 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) - -;; CHECK: (func $c3 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) - -;; CHECK: (func $c4 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) - -;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) - -;; CHECK: (func $c6 (type $1) (result i32) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) +(module ;; tail calls inhibit dropped result removal + ;; CHECK: (type $0 (func (param i32) (result i32))) -;; CHECK: (func $c7 (type $1) (result i32) -;; CHECK-NEXT: (return -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) + ;; CHECK: (type $1 (func (result i32))) -;; CHECK: (func $c8 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 5) -;; CHECK-NEXT: ) -(module ;; tail calls inhibit dropped result removal + ;; CHECK: (func $foo (type $0) (param $x i32) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (return_call $bar) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) (func $foo (param $x i32) (result i32) (drop (return_call $bar @@ -644,290 +415,36 @@ ) (i32.const 42) ) + ;; CHECK: (func $bar (type $1) (result i32) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 7) + ;; CHECK-NEXT: ) (func $bar (param $x i32) (result i32) (i32.const 7) ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (type $1 (func (result i32))) - -;; CHECK: (type $2 (func (param i32))) - -;; CHECK: (type $3 (func (result f64))) - -;; CHECK: (type $4 (func (param f64))) - -;; CHECK: (type $5 (func (param i32) (result i32))) - -;; CHECK: (import "a" "b" (func $get-i32 (type $1) (result i32))) - -;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) - -;; CHECK: (table $0 2 2 funcref) - -;; CHECK: (elem $0 (i32.const 0) $a9 $c8) - -;; CHECK: (export "a8" (func $a8)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: (call $a) -;; CHECK-NEXT: ) - -;; CHECK: (func $a1 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b1 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $b11 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $a2 (type $2) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b2 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b22 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a3 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const -1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b3 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $b33 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $a4 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b4 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b43 (type $0) -;; CHECK-NEXT: (call $a4) -;; CHECK-NEXT: ) - -;; CHECK: (func $a5 (type $0) -;; CHECK-NEXT: (local $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b5 (type $0) -;; CHECK-NEXT: (call $a5) -;; CHECK-NEXT: ) - -;; CHECK: (func $a6 (type $2) (param $0 i32) -;; CHECK-NEXT: (local $1 f64) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b6 (type $0) -;; CHECK-NEXT: (call $a6 -;; CHECK-NEXT: (call $get-i32) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a7 (type $4) (param $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b7 (type $0) -;; CHECK-NEXT: (call $a7 -;; CHECK-NEXT: (call $get-f64) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a8 (type $2) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b8 (type $0) -;; CHECK-NEXT: (call $a8 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a9 (type $2) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b9 (type $0) -;; CHECK-NEXT: (call $a9 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a10 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a11 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: ) - -;; CHECK: (func $a12 (type $2) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c1 (type $0) -;; CHECK-NEXT: (local $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $x -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c5 -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c6) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c7) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c8) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c2 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) - -;; CHECK: (func $c3 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) - -;; CHECK: (func $c4 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) - -;; CHECK: (func $c5 (type $5) (param $x i32) (result i32) -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) - -;; CHECK: (func $c6 (type $1) (result i32) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $c7 (type $1) (result i32) -;; CHECK-NEXT: (return -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c8 (type $1) (result i32) -;; CHECK-NEXT: (i32.const 5) -;; CHECK-NEXT: ) (module ;; indirect tail calls inhibit dropped result removal + ;; CHECK: (type $T (func (result i32))) (type $T (func (result i32))) (table 1 1 funcref) + ;; CHECK: (type $1 (func)) + + ;; CHECK: (table $0 1 1 funcref) + + ;; CHECK: (func $foo (type $T) (result i32) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (return_call_indirect $0 (type $T) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $foo (param $x i32) (result i32) (drop (return_call_indirect (type $T) @@ -935,6 +452,11 @@ ) ) ) + ;; CHECK: (func $bar (type $1) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $foo) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $bar (drop (call $foo @@ -943,600 +465,86 @@ ) ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (type $1 (func (param i32))) - -;; CHECK: (type $2 (func (result i32))) - -;; CHECK: (type $3 (func (result f64))) - -;; CHECK: (type $4 (func (param f64))) - -;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) - -;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) - -;; CHECK: (table $0 2 2 funcref) - -;; CHECK: (elem $0 (i32.const 0) $a9 $c8) - -;; CHECK: (export "a8" (func $a8)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: (call $a) -;; CHECK-NEXT: ) - -;; CHECK: (func $a1 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b1 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $b11 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $a2 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b2 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b22 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a3 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const -1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b3 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $b33 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $a4 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b4 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b43 (type $0) -;; CHECK-NEXT: (call $a4) -;; CHECK-NEXT: ) - -;; CHECK: (func $a5 (type $0) -;; CHECK-NEXT: (local $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b5 (type $0) -;; CHECK-NEXT: (call $a5) -;; CHECK-NEXT: ) - -;; CHECK: (func $a6 (type $1) (param $0 i32) -;; CHECK-NEXT: (local $1 f64) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b6 (type $0) -;; CHECK-NEXT: (call $a6 -;; CHECK-NEXT: (call $get-i32) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a7 (type $4) (param $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b7 (type $0) -;; CHECK-NEXT: (call $a7 -;; CHECK-NEXT: (call $get-f64) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a8 (type $1) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b8 (type $0) -;; CHECK-NEXT: (call $a8 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a9 (type $1) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b9 (type $0) -;; CHECK-NEXT: (call $a9 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a10 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a11 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: ) - -;; CHECK: (func $a12 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c1 (type $0) -;; CHECK-NEXT: (local $x i32) -;; CHECK-NEXT: (call $c2) -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $x -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $c5 -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $c6) -;; CHECK-NEXT: (call $c7) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c8) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c2 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c3 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c4 (type $2) (result i32) -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) +(module + ;; CHECK: (type $0 (func (param funcref i32 f64) (result i64))) -;; CHECK: (func $c5 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) + ;; CHECK: (type $1 (func (param f32) (result funcref))) -;; CHECK: (func $c6 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) + ;; CHECK: (elem declare func $0) -;; CHECK: (func $c7 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (return) -;; CHECK-NEXT: ) + ;; CHECK: (export "export" (func $export)) -;; CHECK: (func $c8 (type $2) (result i32) -;; CHECK-NEXT: (i32.const 5) -;; CHECK-NEXT: ) -(module + ;; CHECK: (func $0 (type $0) (param $0 funcref) (param $1 i32) (param $2 f64) (result i64) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $0 (param $0 funcref) (param $1 i32) (param $2 f64) (result i64) (nop) (unreachable) ) + ;; CHECK: (func $export (type $1) (param $0 f32) (result funcref) + ;; CHECK-NEXT: (ref.func $0) + ;; CHECK-NEXT: ) (func $export (export "export") (param $0 f32) (result funcref) ;; a ref.func should prevent us from changing the type of a function, as it ;; may escape (ref.func $0) ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (type $1 (func (param i32))) - -;; CHECK: (type $2 (func (result i32))) - -;; CHECK: (type $3 (func (result f64))) - -;; CHECK: (type $4 (func (param f64))) - -;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32))) - -;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64))) - -;; CHECK: (table $0 2 2 funcref) - -;; CHECK: (elem $0 (i32.const 0) $a9 $c8) - -;; CHECK: (export "a8" (func $a8)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: (call $a) -;; CHECK-NEXT: ) - -;; CHECK: (func $a1 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b1 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $b11 (type $0) -;; CHECK-NEXT: (call $a1) -;; CHECK-NEXT: ) - -;; CHECK: (func $a2 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b2 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b22 (type $0) -;; CHECK-NEXT: (call $a2 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a3 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const -1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b3 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $b33 (type $0) -;; CHECK-NEXT: (call $a3) -;; CHECK-NEXT: ) - -;; CHECK: (func $a4 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b4 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $b43 (type $0) -;; CHECK-NEXT: (call $a4) -;; CHECK-NEXT: ) - -;; CHECK: (func $a5 (type $0) -;; CHECK-NEXT: (local $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b5 (type $0) -;; CHECK-NEXT: (call $a5) -;; CHECK-NEXT: ) - -;; CHECK: (func $a6 (type $1) (param $0 i32) -;; CHECK-NEXT: (local $1 f64) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (f64.const 3.14159) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b6 (type $0) -;; CHECK-NEXT: (call $a6 -;; CHECK-NEXT: (call $get-i32) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a7 (type $4) (param $0 f64) -;; CHECK-NEXT: (local $1 i32) -;; CHECK-NEXT: (local.set $1 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b7 (type $0) -;; CHECK-NEXT: (call $a7 -;; CHECK-NEXT: (call $get-f64) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a8 (type $1) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b8 (type $0) -;; CHECK-NEXT: (call $a8 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a9 (type $1) (param $x i32) -;; CHECK-NEXT: ) - -;; CHECK: (func $b9 (type $0) -;; CHECK-NEXT: (call $a9 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a10 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (block -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: (call $a10) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $a11 (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: (call $a11) -;; CHECK-NEXT: ) - -;; CHECK: (func $a12 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $a12 -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c1 (type $0) -;; CHECK-NEXT: (local $x i32) -;; CHECK-NEXT: (call $c2) -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: (call $c3) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.set $x -;; CHECK-NEXT: (call $c4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $c5 -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (call $c6) -;; CHECK-NEXT: (call $c7) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $c8) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c2 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c3 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c4 (type $2) (result i32) -;; CHECK-NEXT: (i32.const 3) -;; CHECK-NEXT: ) - -;; CHECK: (func $c5 (type $1) (param $x i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $c6 (type $0) -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) - -;; CHECK: (func $c7 (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (i32.const 4) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (return) -;; CHECK-NEXT: ) - -;; CHECK: (func $c8 (type $2) (result i32) -;; CHECK-NEXT: (i32.const 5) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $i64 (func (param i64))) (type $i64 (func (param i64))) + ;; CHECK: (type $1 (func)) + + ;; CHECK: (global $global$0 (ref $i64) (ref.func $0)) (global $global$0 (ref $i64) (ref.func $0)) + ;; CHECK: (export "even" (func $1)) (export "even" (func $1)) ;; the argument to this function cannot be removed due to the ref.func of it ;; in a global + ;; CHECK: (func $0 (type $i64) (param $0 i64) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) (func $0 (param $0 i64) (unreachable) ) + ;; CHECK: (func $1 (type $1) + ;; CHECK-NEXT: (call_ref $i64 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $1 (call_ref $i64 (i64.const 0) (global.get $global$0) ) ) + ;; CHECK: (func $2 (type $1) + ;; CHECK-NEXT: (call $0 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $2 (call $0 (i64.const 0) ) ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (type $1 (func (param i32) (result i32))) - -;; CHECK: (export "a" (func $a)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $b -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $1) (param $x i32) (result i32) -;; CHECK-NEXT: (local.get $x) -;; CHECK-NEXT: ) (module ;; a removable non-nullable parameter + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $0 (type $0) + ;; CHECK-NEXT: (local $0 i31ref) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) (func $0 (param $x i31ref) (nop) ) + ;; CHECK: (func $1 (type $0) + ;; CHECK-NEXT: (call $0) + ;; CHECK-NEXT: ) (func $1 (call $0 (ref.i31 (i32.const 0)) @@ -1546,38 +554,56 @@ ;; Arguments that read an immutable global can be optimized, as that is a ;; constant value. -;; CHECK: (type $0 (func)) - -;; CHECK: (type $1 (func (result i32))) +(module + ;; CHECK: (type $0 (func)) -;; CHECK: (export "a" (func $a)) + ;; CHECK: (type $1 (func (param i32))) -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $b) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) + ;; CHECK: (type $2 (func (param i32 i32))) -;; CHECK: (func $b (type $1) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -(module + ;; CHECK: (global $immut i32 (i32.const 42)) (global $immut i32 (i32.const 42)) + ;; CHECK: (global $immut2 i32 (i32.const 43)) (global $immut2 i32 (i32.const 43)) + ;; CHECK: (global $mut (mut i32) (i32.const 1337)) (global $mut (mut i32) (i32.const 1337)) + ;; CHECK: (func $foo (type $1) (param $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $foo (param $x i32) (param $y i32) ;; "Use" the params to avoid other optimizations kicking in. (drop (local.get $x)) (drop (local.get $y)) ) + ;; CHECK: (func $foo-caller (type $0) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $foo-caller ;; Note how the mutable param has a different value in each call, which shows ;; the reason that we cannot optimize in this case. But we can optimize the @@ -1594,11 +620,35 @@ ) ) + ;; CHECK: (func $bar (type $2) (param $x i32) (param $y i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $bar (param $x i32) (param $y i32) (drop (local.get $x)) (drop (local.get $y)) ) + ;; CHECK: (func $bar-caller (type $0) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: (global.get $immut2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $bar-caller ;; Corner cases of mixing mutable with immutable and mixing two immutables. (global.set $mut (i32.const 1)) @@ -1614,26 +664,28 @@ ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (export "a" (func $a)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (call $b) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (block (result i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $0 (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $0 (param $0 i32) (result i32) ;; The returns here are nested in each other, and one is a recursive call to ;; this function itself, which makes this a corner case we might emit invalid @@ -1651,6 +703,9 @@ ) ) + ;; CHECK: (func $other-call (type $0) + ;; CHECK-NEXT: (call $0) + ;; CHECK-NEXT: ) (func $other-call (drop (call $0 @@ -1660,28 +715,20 @@ ) ) -;; CHECK: (type $0 (func)) - -;; CHECK: (export "a" (func $a)) - -;; CHECK: (func $a (type $0) -;; CHECK-NEXT: (call $b) -;; CHECK-NEXT: ) - -;; CHECK: (func $b (type $0) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (block (result i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $A (func (result (ref $A)))) (type $A (func (result (ref $A)))) + ;; CHECK: (type $1 (func)) + + ;; CHECK: (func $no-caller (type $A) (result (ref $A)) + ;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nofunc) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $no-caller (type $A) (result (ref $A)) ;; This return_call is to a bottom type, which we should ignore and not error ;; on. There is nothing to optimize here (other passes will turn this call @@ -1692,6 +739,11 @@ ) ) + ;; CHECK: (func $caller (type $1) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $no-caller) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $caller (drop (call $no-caller) @@ -1699,21 +751,28 @@ ) ) -;; CHECK: (type $0 (func (param i32) (result i32))) - -;; CHECK: (func $foo (type $0) (param $x i32) (result i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (return_call $bar -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $0) (param $x i32) (result i32) -;; CHECK-NEXT: (i32.const 7) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $0 (func (param f64) (result i32))) + + ;; CHECK: (func $target (type $0) (param $0 f64) (result i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (f64.const 1.1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (f64.const 4.4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $target (param $a i32) (param $b f64) (param $c i32) (result i32) ;; Test removing a parameter despite calls having interesting non-unreachable ;; effects. This also tests recursion of such calls. We can remove all the i32 @@ -1734,29 +793,30 @@ ) ) -;; CHECK: (type $0 (func (param i32) (result i32))) - -;; CHECK: (type $1 (func (result i32))) - -;; CHECK: (func $foo (type $0) (param $x i32) (result i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (return_call $bar) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $1) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const 7) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $v128 (func (result v128))) (type $v128 (func (result v128))) + ;; CHECK: (type $2 (func (result f32))) + + ;; CHECK: (table $0 10 funcref) (table $0 10 funcref) + ;; CHECK: (func $caller-effects (type $0) + ;; CHECK-NEXT: (local $0 v128) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result f32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (call_indirect $0 (type $v128) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $target) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $caller-effects (drop (call $target @@ -1771,31 +831,54 @@ ) ) + ;; CHECK: (func $target (type $2) (result f32) + ;; CHECK-NEXT: (local $0 i64) + ;; CHECK-NEXT: (local $1 i64) + ;; CHECK-NEXT: (local $2 v128) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $target (param $0 i64) (param $1 v128) (param $2 i64) (result f32) ;; All parameters here should vanish. (unreachable) ) ) -;; CHECK: (type $0 (func (param i32) (result i32))) - -;; CHECK: (type $1 (func (result i32))) - -;; CHECK: (func $foo (type $0) (param $x i32) (result i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (return_call $bar) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $1) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.const 7) -;; CHECK-NEXT: ) (module + ;; CHECK: (type $0 (func (param i32 i64))) + + ;; CHECK: (type $1 (func (param i64 i64))) + + ;; CHECK: (func $caller-later-br (type $0) (param $x i32) (param $y i64) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $block) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $caller-later-br (param $x i32) (param $y i64) (block $block (drop @@ -1832,6 +915,20 @@ ) ) + ;; CHECK: (func $target (type $1) (param $0 i64) (param $1 i64) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result f32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $target (param $0 i64) (param $1 i32) (param $2 i64) (result f32) ;; The i32 parameter should vanish. (drop @@ -1844,30 +941,18 @@ ) ) -;; CHECK: (type $0 (func (param i32) (result i32))) - -;; CHECK: (type $T (func (result i32))) - -;; CHECK: (type $2 (func)) - -;; CHECK: (table $0 1 1 funcref) +(module + ;; CHECK: (type $0 (func)) -;; CHECK: (func $foo (type $0) (param $x i32) (result i32) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (return_call_indirect $0 (type $T) -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) + ;; CHECK: (type $1 (func (param i32))) -;; CHECK: (func $bar (type $2) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $foo -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -(module + ;; CHECK: (func $target (type $0) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $target (param $0 i32) ;; The parameter here is unused: there is a get, but it is unreachable. We can ;; remove the parameter here, and in the caller below. @@ -1877,32 +962,12 @@ ) ) + ;; CHECK: (func $caller (type $1) (param $x i32) + ;; CHECK-NEXT: (call $target) + ;; CHECK-NEXT: ) (func $caller (param $x i32) (call $target (local.get $x) ) ) ) -;; CHECK: (type $T (func (result i32))) - -;; CHECK: (type $1 (func)) - -;; CHECK: (table $0 1 1 funcref) - -;; CHECK: (func $foo (type $T) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (return_call_indirect $0 (type $T) -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $bar (type $1) -;; CHECK-NEXT: (drop -;; CHECK-NEXT: (call $foo) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae_tnh.wast b/test/lit/passes/dae_tnh.wast index c7d0347a9f2..6e78283fdce 100644 --- a/test/lit/passes/dae_tnh.wast +++ b/test/lit/passes/dae_tnh.wast @@ -6,11 +6,12 @@ ;; CHECK: (type $struct (sub (struct (field i32)))) (type $struct (sub (struct (field i32)))) - ;; CHECK: (type $1 (func (param i32))) + ;; CHECK: (type $1 (func)) ;; CHECK: (type $2 (func (param (ref null $struct)))) - ;; CHECK: (func $target (type $1) (param $x i32) + ;; CHECK: (func $target (type $1) + ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $target (param $x i32) @@ -18,11 +19,7 @@ ) ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (struct.get $struct 0 - ;; CHECK-NEXT: (local.get $ref) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $target) ;; CHECK-NEXT: ) (func $caller (param $ref (ref null $struct)) (call $target @@ -37,19 +34,12 @@ ) (module - ;; CHECK: (type $struct (sub (struct (field i32)))) - - ;; CHECK: (type $1 (func)) - - ;; CHECK: (type $2 (func (param (ref null $struct)))) + ;; CHECK: (type $0 (func)) - ;; CHECK: (func $target (type $1) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) + ;; CHECK: (type $1 (func (param i32))) - ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) - ;; CHECK-NEXT: (call $target) + ;; CHECK: (func $caller (type $0) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $caller ;; Removing this parameter would make the type of the call change from @@ -66,25 +56,20 @@ ) ) + ;; CHECK: (func $target (type $1) (param $0 i32) + ;; CHECK-NEXT: ) (func $target (param i32) ) ) ;; As above but the called target has a result. (module - ;; CHECK: (type $struct (sub (struct (field i32)))) + ;; CHECK: (type $0 (func (result i32))) - ;; CHECK: (type $1 (func)) - - ;; CHECK: (type $2 (func (param (ref null $struct)))) + ;; CHECK: (type $1 (func (param i32) (result i32))) - ;; CHECK: (func $target (type $1) - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - - ;; CHECK: (func $caller (type $2) (param $ref (ref null $struct)) - ;; CHECK-NEXT: (call $target) + ;; CHECK: (func $caller (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $caller (result i32) ;; Again, the call is replaced by an unreachable. @@ -93,6 +78,9 @@ ) ) + ;; CHECK: (func $target (type $1) (param $0 i32) (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) (func $target (param i32) (result i32) (i32.const 42) ) @@ -107,9 +95,7 @@ ;; CHECK: (type $1 (func (param i32))) ;; CHECK: (func $caller (type $0) - ;; CHECK-NEXT: (call $target - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $caller (return_call $target @@ -124,15 +110,18 @@ ) (module - ;; CHECK: (type $0 (func)) + ;; CHECK: (type $0 (func (param i32))) - ;; CHECK: (type $1 (func (param i32))) - - ;; CHECK: (func $caller (type $0) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) + ;; CHECK: (type $1 (func)) - ;; CHECK: (func $target (type $1) (param $0 i32) + ;; CHECK: (func $target (type $0) (param $0 i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (f64.const 4.2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $target (param $used i32) (param $unused f64) ;; One parameter is used, and one is not. @@ -141,6 +130,11 @@ ) ) + ;; CHECK: (func $caller (type $1) + ;; CHECK-NEXT: (call $target + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $caller ;; There is an unreachable parameter, and as in the cases above, we can't ;; remove it as it would change the type. But it isn't the param we want to From bad7b8442dcf39e4593415ddca1c14f2279fe130 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 1 Dec 2025 16:57:29 -0800 Subject: [PATCH 11/44] clean --- src/passes/DeadArgumentElimination.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index e1f67e653b0..422973bc521 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -1,3 +1,4 @@ +// 16.8,16.7,16.75 /* * Copyright 2018 WebAssembly Community Group participants * @@ -319,7 +320,7 @@ struct DAE : public Pass { } } -std::cerr << "iter\n" << *module << '\n'; +//std::cerr << "iter\n" << *module << '\n'; // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls @@ -329,7 +330,7 @@ std::cerr << "iter\n" << *module << '\n'; std::unordered_set calledByJustUpdated; for (auto& [func, info] : infoMap) { if (info.justUpdated) { -std::cerr << "just updated " << func << '\n'; +//std::cerr << "just updated " << func << '\n'; auto index = indexes[func]; justUpdated.insert(index); for (auto& callee : callees[index]) { @@ -369,7 +370,7 @@ std::cerr << "just updated " << func << '\n'; // calls, after which that data structure is up-to-date. for (auto& caller : justUpdated) { auto& info = infoMap[module->functions[caller]->name]; -std::cerr << "processing calls from " << caller << '\n'; +//std::cerr << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]].calls; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); @@ -482,7 +483,7 @@ std::cerr << "processing calls from " << caller << '\n'; auto [removedIndexes, outcome] = ParamUtils::removeParameters( {func}, infoMap[name].unusedParams, calls, {}, module, getPassRunner()); if (!removedIndexes.empty()) { -std::cerr << "remove param " << func->name << '\n'; +//std::cerr << "remove param " << func->name << '\n'; // Success! worthOptimizing.insert(func); markStale(name); From efdf8e85af9fc9d4a8619e96773721298cbaf8d4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 11:09:54 -0800 Subject: [PATCH 12/44] timer --- src/passes/DeadArgumentElimination.cpp | 42 +++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 422973bc521..c72751adcf4 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -1,4 +1,7 @@ // 16.8,16.7,16.75 +// => +// 14.5 +// not a big... win /* * Copyright 2018 WebAssembly Community Group participants * @@ -51,6 +54,7 @@ #include "pass.h" #include "passes/opt-utils.h" #include "support/sorted_vector.h" +#include "support/timing.h" #include "wasm-builder.h" #include "wasm.h" @@ -189,6 +193,8 @@ struct DAEScanner } }; +static Timer scan("scan"), combine("combine"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); + struct DAE : public Pass { // This pass changes locals and parameters. // FIXME DWARF updating does not handle local changes yet. @@ -236,6 +242,17 @@ struct DAE : public Pass { break; } } + + scan.dump(); + combine.dump(); + opt1.dump(); + opt2.dump(); + opt3.dump(); + opt4.dump(); + loc.dump(); + oai.dump(); + allC.dump(); + } // For each function, the set of callers. This is used to propagate changes, @@ -255,6 +272,8 @@ struct DAE : public Pass { std::vector> callees; bool iteration(Module* module, DAEFunctionInfoMap& infoMap) { + std::cout << "iter\n"; + allDroppedCalls.clear(); #if DAE_DEBUG @@ -272,11 +291,17 @@ struct DAE : public Pass { } #endif +scan.start(); + DAEScanner scanner(&infoMap); scanner.walkModuleCode(module); // Scan all the functions. scanner.run(getPassRunner(), module); +scan.stop(); + +combine.start(); + // Combine all the info from the scan. std::vector tailCallees(numFunctions); std::vector hasUnseenCalls(numFunctions); @@ -320,7 +345,9 @@ struct DAE : public Pass { } } -//std::cerr << "iter\n" << *module << '\n'; +combine.stop(); + +allC.start(); // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls @@ -382,6 +409,7 @@ struct DAE : public Pass { } } +allC.stop(); // Track which functions we changed that are worth re-optimizing at the end. std::unordered_set worthOptimizing; @@ -415,6 +443,7 @@ struct DAE : public Pass { } }; +opt1.start(); // We now have a mapping of all call sites for each function, and can look // for optimization opportunities. for (Index index = 0; index < numFunctions; index++) { @@ -456,12 +485,16 @@ struct DAE : public Pass { markStale(func->name); } } +opt1.stop(); if (refinedReturnTypes) { +opt2.start(); // Changing a call expression's return type can propagate out to its // parents, and so we must refinalize. // TODO: We could track in which functions we actually make changes. ReFinalize().run(getPassRunner(), module); +opt2.stop(); } +opt3.start(); // We now know which parameters are unused, and can potentially remove them. for (Index index = 0; index < numFunctions; index++) { auto* func = module->functions[index].get(); @@ -493,6 +526,8 @@ struct DAE : public Pass { callTargetsToLocalize.insert(name); } } +opt3.stop(); +opt4.start(); // We can also tell which calls have all their return values dropped. Note // that we can't do this if we changed anything so far, as we may have // modified allCalls (we can't modify a call site twice in one iteration, @@ -540,14 +575,19 @@ struct DAE : public Pass { markCallersStale(index); } } +opt4.stop(); if (!callTargetsToLocalize.empty()) { +loc.start(); ParamUtils::localizeCallsTo( callTargetsToLocalize, *module, getPassRunner(), [&](Function* func) { markStale(func->name); }); +loc.stop(); } if (optimize && !worthOptimizing.empty()) { +oai.start(); OptUtils::optimizeAfterInlining(worthOptimizing, module, getPassRunner()); +oai.stop(); } return !worthOptimizing.empty() || refinedReturnTypes || From 765950a8fb5ed25357dd3333ff6f6df124c00422 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 11:16:59 -0800 Subject: [PATCH 13/44] timer --- src/passes/DeadArgumentElimination.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index c72751adcf4..68c4ed066ed 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -245,6 +245,7 @@ struct DAE : public Pass { scan.dump(); combine.dump(); + callers.dump(); opt1.dump(); opt2.dump(); opt3.dump(); @@ -324,6 +325,9 @@ combine.start(); } } +combine.stop(); + +callers.start(); // See comment above, we compute callers once and never again. if (callers.empty()) { // Compute first as sets, to deduplicate. @@ -344,8 +348,7 @@ combine.start(); } } } - -combine.stop(); +callers.stop(); allC.start(); From caf506ddbe1cbad7eddda44d250c21df2e39dd16 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 11:17:33 -0800 Subject: [PATCH 14/44] timer --- src/passes/DeadArgumentElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 68c4ed066ed..185e9b2a9b9 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -193,7 +193,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), callers("callers"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); struct DAE : public Pass { // This pass changes locals and parameters. From 4fb84779d31d2401e662bbde270ad7e91e6ece38 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 11:18:55 -0800 Subject: [PATCH 15/44] timer --- src/passes/DeadArgumentElimination.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 185e9b2a9b9..a1820b15c77 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -193,7 +193,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), callers("callers"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), callerz("callers"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); struct DAE : public Pass { // This pass changes locals and parameters. @@ -245,7 +245,7 @@ struct DAE : public Pass { scan.dump(); combine.dump(); - callers.dump(); + callerz.dump(); opt1.dump(); opt2.dump(); opt3.dump(); @@ -327,7 +327,7 @@ combine.start(); combine.stop(); -callers.start(); +callerz.start(); // See comment above, we compute callers once and never again. if (callers.empty()) { // Compute first as sets, to deduplicate. @@ -348,7 +348,7 @@ callers.start(); } } } -callers.stop(); +callerz.stop(); allC.start(); From 553186e26c41f85966896abbbe1dca54d3138689 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 12:50:17 -0800 Subject: [PATCH 16/44] timer --- src/passes/DeadArgumentElimination.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index a1820b15c77..40e9690e10b 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -193,7 +193,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), callerz("callers"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); struct DAE : public Pass { // This pass changes locals and parameters. @@ -368,13 +368,13 @@ allC.start(); } } } +std::cout << " updating allcalls justUpdated=" << justUpdated << ", called by them=" << calledByJustUpdated << " out of " << numFunctions << '\n'; // For each such called function, we don't want to alter calls from // unchanged functions. That is, if X calls C and D in the example above, // and X is not just-updated, then X's calls to C and D are fine as they // are. Leaving such calls alone, remove calls from the callers that we did // just update, and after that, add them from the fresh data we have on // those just-updated functions. - std::unordered_set relevantCallers; for (auto& called : calledByJustUpdated) { auto& calledCalls = allCalls[called]; auto oldSize = calledCalls.calls.size(); From 6dcf2adba1c4b6fc7413a640e1e90b311a62e101 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 12:50:45 -0800 Subject: [PATCH 17/44] timer --- src/passes/DeadArgumentElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 40e9690e10b..c36d10ce287 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -368,7 +368,7 @@ allC.start(); } } } -std::cout << " updating allcalls justUpdated=" << justUpdated << ", called by them=" << calledByJustUpdated << " out of " << numFunctions << '\n'; +std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; // For each such called function, we don't want to alter calls from // unchanged functions. That is, if X calls C and D in the example above, // and X is not just-updated, then X's calls to C and D are fine as they From 98633332d881b12502b0dab3a85ea12eac561010 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 12:58:11 -0800 Subject: [PATCH 18/44] timer --- src/passes/DeadArgumentElimination.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index c36d10ce287..895329d3955 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -2,6 +2,7 @@ // => // 14.5 // not a big... win +// allC is 1.65 /* * Copyright 2018 WebAssembly Community Group participants * @@ -369,6 +370,16 @@ allC.start(); } } std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; + if (justUpdated.size() + calledByJustUpdated.size() > numFunctions / 10) { + // Many functions need to be processed to do an incremental update. Just + // do a full recompute from scratch, which may be faster. + allCalls.clear(); + allCalls.resize(numFunctions); + // Clear this data structure so we don't process it below. + calledByJustUpdated.clear(); + } else { + // Do an incremental update. + } // For each such called function, we don't want to alter calls from // unchanged functions. That is, if X calls C and D in the example above, // and X is not just-updated, then X's calls to C and D are fine as they From 5cf0f0cf72bd6fa62ac9215a56573cade460f7b2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:01:23 -0800 Subject: [PATCH 19/44] timer --- src/passes/DeadArgumentElimination.cpp | 91 ++++++++++++++------------ 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 895329d3955..d71a56b614e 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -369,47 +369,8 @@ allC.start(); } } } -std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; - if (justUpdated.size() + calledByJustUpdated.size() > numFunctions / 10) { - // Many functions need to be processed to do an incremental update. Just - // do a full recompute from scratch, which may be faster. - allCalls.clear(); - allCalls.resize(numFunctions); - // Clear this data structure so we don't process it below. - calledByJustUpdated.clear(); - } else { - // Do an incremental update. - } - // For each such called function, we don't want to alter calls from - // unchanged functions. That is, if X calls C and D in the example above, - // and X is not just-updated, then X's calls to C and D are fine as they - // are. Leaving such calls alone, remove calls from the callers that we did - // just update, and after that, add them from the fresh data we have on - // those just-updated functions. - for (auto& called : calledByJustUpdated) { - auto& calledCalls = allCalls[called]; - auto oldSize = calledCalls.calls.size(); - assert(oldSize == calledCalls.origins.size()); - Index skip = 0; - for (Index i = 0; i < calledCalls.calls.size(); i++) { - if (justUpdated.count(calledCalls.origins[i])) { - // Remove it by skipping over. - skip++; - } else if (skip) { - // Keep it by writing to the proper place. - calledCalls.calls[i - skip] = calledCalls.calls[i]; - calledCalls.origins[i - skip] = calledCalls.origins[i]; - } - } - if (skip > 0) { - // Update the sizes after removing things. - calledCalls.calls.resize(oldSize - skip); - calledCalls.origins.resize(oldSize - skip); - } - } - // The just-updated callers have been cleaned out of |allCalls|. Add their - // calls, after which that data structure is up-to-date. - for (auto& caller : justUpdated) { + + auto addCallsFrom = [&](Index caller) { auto& info = infoMap[module->functions[caller]->name]; //std::cerr << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { @@ -421,8 +382,54 @@ std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", call origins.push_back(caller); } } - } + }; +std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; + if (justUpdated.size() + calledByJustUpdated.size() > numFunctions / 10) { + // Many functions need to be processed to do an incremental update. Just + // do a full recompute from scratch, which may be faster. + allCalls.clear(); + allCalls.resize(numFunctions); + for (Index caller = 0; caller < numFunctions; caller++) { + addCallsFrom(caller); + } + } else { + // Do an incremental update. + std::cout << " incrememt\n"; + // For each such called function, we don't want to alter calls from + // unchanged functions. That is, if X calls C and D in the example above, + // and X is not just-updated, then X's calls to C and D are fine as they + // are. Leaving such calls alone, remove calls from the callers that we did + // just update, and after that, add them from the fresh data we have on + // those just-updated functions. + for (auto& called : calledByJustUpdated) { + auto& calledCalls = allCalls[called]; + auto oldSize = calledCalls.calls.size(); + assert(oldSize == calledCalls.origins.size()); + Index skip = 0; + for (Index i = 0; i < calledCalls.calls.size(); i++) { + if (justUpdated.count(calledCalls.origins[i])) { + // Remove it by skipping over. + skip++; + } else if (skip) { + // Keep it by writing to the proper place. + calledCalls.calls[i - skip] = calledCalls.calls[i]; + calledCalls.origins[i - skip] = calledCalls.origins[i]; + } + } + if (skip > 0) { + // Update the sizes after removing things. + calledCalls.calls.resize(oldSize - skip); + calledCalls.origins.resize(oldSize - skip); + } + } + // The just-updated callers have been cleaned out of |allCalls|. Add their + // calls, after which that data structure is up-to-date. + for (auto& caller : justUpdated) { + addCallsFrom(caller); + } + } + allC.stop(); // Track which functions we changed that are worth re-optimizing at the end. std::unordered_set worthOptimizing; From 35b31b7d8a0b1ca6629b564acdf08c293898e734 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:07:10 -0800 Subject: [PATCH 20/44] timer --- src/passes/DeadArgumentElimination.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index d71a56b614e..9e8aa6dc249 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -3,6 +3,11 @@ // 14.5 // not a big... win // allC is 1.65 +/* + / 10 => 2.03 :( also / 5 + / 2 => 1.58, 1.62 + ... => 1.51 +*/ /* * Copyright 2018 WebAssembly Community Group participants * From 3b2e5dbd4715cca700af61dc97dc025e4f62c001 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:08:52 -0800 Subject: [PATCH 21/44] timer --- src/passes/DeadArgumentElimination.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 9e8aa6dc249..e446b82586d 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -2,12 +2,7 @@ // => // 14.5 // not a big... win -// allC is 1.65 -/* - / 10 => 2.03 :( also / 5 - / 2 => 1.58, 1.62 - ... => 1.51 -*/ +// allC is 1.5 /* * Copyright 2018 WebAssembly Community Group participants * @@ -390,7 +385,7 @@ allC.start(); }; std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; - if (justUpdated.size() + calledByJustUpdated.size() > numFunctions / 10) { + if (justUpdated.size() + calledByJustUpdated.size() >= numFunctions) { // Many functions need to be processed to do an incremental update. Just // do a full recompute from scratch, which may be faster. allCalls.clear(); From 1df1294b47f0706a633cbf7ad06c8df31a609acc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:11:59 -0800 Subject: [PATCH 22/44] timer --- src/passes/DeadArgumentElimination.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index e446b82586d..8349872eaab 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -194,7 +194,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), combine2("combine2"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); struct DAE : public Pass { // This pass changes locals and parameters. @@ -246,6 +246,7 @@ struct DAE : public Pass { scan.dump(); combine.dump(); + combine2.dump(); callerz.dump(); opt1.dump(); opt2.dump(); @@ -312,9 +313,6 @@ combine.start(); for (auto& callee : info.tailCallees) { tailCallees[indexes[callee]] = true; } - for (auto& [call, dropp] : info.droppedCalls) { - allDroppedCalls[call] = dropp; - } for (auto& name : info.hasUnseenCalls) { hasUnseenCalls[indexes[name]] = true; } @@ -328,6 +326,14 @@ combine.start(); combine.stop(); +combine2.start(); + for (auto& [_, info] : infoMap) { + for (auto& [call, dropp] : info.droppedCalls) { + allDroppedCalls[call] = dropp; + } + } +combine2.stop(); + callerz.start(); // See comment above, we compute callers once and never again. if (callers.empty()) { From 3f90d40385c90b21b8ded45e6364e318763f4dba Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:17:03 -0800 Subject: [PATCH 23/44] timer --- src/passes/DeadArgumentElimination.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 8349872eaab..e446b82586d 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -194,7 +194,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), combine2("combine2"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); struct DAE : public Pass { // This pass changes locals and parameters. @@ -246,7 +246,6 @@ struct DAE : public Pass { scan.dump(); combine.dump(); - combine2.dump(); callerz.dump(); opt1.dump(); opt2.dump(); @@ -313,6 +312,9 @@ combine.start(); for (auto& callee : info.tailCallees) { tailCallees[indexes[callee]] = true; } + for (auto& [call, dropp] : info.droppedCalls) { + allDroppedCalls[call] = dropp; + } for (auto& name : info.hasUnseenCalls) { hasUnseenCalls[indexes[name]] = true; } @@ -326,14 +328,6 @@ combine.start(); combine.stop(); -combine2.start(); - for (auto& [_, info] : infoMap) { - for (auto& [call, dropp] : info.droppedCalls) { - allDroppedCalls[call] = dropp; - } - } -combine2.stop(); - callerz.start(); // See comment above, we compute callers once and never again. if (callers.empty()) { From cfa5891addb99babbfce2f9be1276090d0eec4d8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:21:44 -0800 Subject: [PATCH 24/44] timer --- src/passes/DeadArgumentElimination.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index e446b82586d..c577e2fc7e2 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -194,7 +194,7 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"); +static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"), ra("ra"), rr("rr"), acv("acv"); struct DAE : public Pass { // This pass changes locals and parameters. @@ -254,6 +254,9 @@ struct DAE : public Pass { loc.dump(); oai.dump(); allC.dump(); + ra.dump(); + rr.dump(); + avc.dump(); } @@ -484,17 +487,22 @@ opt1.start(); // Refine argument types before doing anything else. This does not // affect whether an argument is used or not, it just refines the type // where possible. +ra.start(); auto name = func->name; if (refineArgumentTypes(func, calls, module, infoMap[name])) { worthOptimizing.insert(func); markStale(func->name); } // Refine return types as well. +ra.stop(); +rr.start(); if (refineReturnTypes(func, calls, module)) { refinedReturnTypes = true; markStale(name); markCallersStale(index); } +rr.stop(); +acv.start(); auto optimizedIndexes = ParamUtils::applyConstantValues({func}, calls, {}, module); for (auto i : optimizedIndexes) { @@ -502,6 +510,7 @@ opt1.start(); // for that). infoMap[name].unusedParams.insert(i); } +acv.stop(); if (!optimizedIndexes.empty()) { markStale(func->name); } From bf3eb424195105a8996ab5ce7f07315bf01f60d8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 13:21:56 -0800 Subject: [PATCH 25/44] timer --- src/passes/DeadArgumentElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index c577e2fc7e2..b4d6944e8e6 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -256,7 +256,7 @@ struct DAE : public Pass { allC.dump(); ra.dump(); rr.dump(); - avc.dump(); + acv.dump(); } From 20a5743c742930a9ac2bf352c0f173b2772b682b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:40:32 -0800 Subject: [PATCH 26/44] fix --- src/passes/DeadArgumentElimination.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index b4d6944e8e6..816f2dcd90f 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -1,8 +1,3 @@ -// 16.8,16.7,16.75 -// => -// 14.5 -// not a big... win -// allC is 1.5 /* * Copyright 2018 WebAssembly Community Group participants * @@ -55,7 +50,6 @@ #include "pass.h" #include "passes/opt-utils.h" #include "support/sorted_vector.h" -#include "support/timing.h" #include "wasm-builder.h" #include "wasm.h" @@ -194,8 +188,6 @@ struct DAEScanner } }; -static Timer scan("scan"), combine("combine"), callerz("callerz"), opt1("opt1"), opt2("opt2"), opt3("opt3"), opt4("opt4"), loc("loc"), oai("oai"),allC("allC"), ra("ra"), rr("rr"), acv("acv"); - struct DAE : public Pass { // This pass changes locals and parameters. // FIXME DWARF updating does not handle local changes yet. @@ -243,21 +235,6 @@ struct DAE : public Pass { break; } } - - scan.dump(); - combine.dump(); - callerz.dump(); - opt1.dump(); - opt2.dump(); - opt3.dump(); - opt4.dump(); - loc.dump(); - oai.dump(); - allC.dump(); - ra.dump(); - rr.dump(); - acv.dump(); - } // For each function, the set of callers. This is used to propagate changes, From a1a64130379adc76328d5fba7370c4c30623d689 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:40:58 -0800 Subject: [PATCH 27/44] fix --- src/passes/DeadArgumentElimination.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 816f2dcd90f..6828c1c2613 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -273,7 +273,6 @@ struct DAE : public Pass { } #endif -scan.start(); DAEScanner scanner(&infoMap); scanner.walkModuleCode(module); @@ -282,7 +281,6 @@ scan.start(); scan.stop(); -combine.start(); // Combine all the info from the scan. std::vector tailCallees(numFunctions); @@ -308,7 +306,6 @@ combine.start(); combine.stop(); -callerz.start(); // See comment above, we compute callers once and never again. if (callers.empty()) { // Compute first as sets, to deduplicate. @@ -331,7 +328,6 @@ callerz.start(); } callerz.stop(); -allC.start(); // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls @@ -444,7 +440,6 @@ allC.stop(); } }; -opt1.start(); // We now have a mapping of all call sites for each function, and can look // for optimization opportunities. for (Index index = 0; index < numFunctions; index++) { @@ -464,7 +459,6 @@ opt1.start(); // Refine argument types before doing anything else. This does not // affect whether an argument is used or not, it just refines the type // where possible. -ra.start(); auto name = func->name; if (refineArgumentTypes(func, calls, module, infoMap[name])) { worthOptimizing.insert(func); @@ -472,14 +466,12 @@ ra.start(); } // Refine return types as well. ra.stop(); -rr.start(); if (refineReturnTypes(func, calls, module)) { refinedReturnTypes = true; markStale(name); markCallersStale(index); } rr.stop(); -acv.start(); auto optimizedIndexes = ParamUtils::applyConstantValues({func}, calls, {}, module); for (auto i : optimizedIndexes) { @@ -494,14 +486,12 @@ acv.stop(); } opt1.stop(); if (refinedReturnTypes) { -opt2.start(); // Changing a call expression's return type can propagate out to its // parents, and so we must refinalize. // TODO: We could track in which functions we actually make changes. ReFinalize().run(getPassRunner(), module); opt2.stop(); } -opt3.start(); // We now know which parameters are unused, and can potentially remove them. for (Index index = 0; index < numFunctions; index++) { auto* func = module->functions[index].get(); @@ -534,7 +524,6 @@ opt3.start(); } } opt3.stop(); -opt4.start(); // We can also tell which calls have all their return values dropped. Note // that we can't do this if we changed anything so far, as we may have // modified allCalls (we can't modify a call site twice in one iteration, @@ -584,7 +573,6 @@ opt4.start(); } opt4.stop(); if (!callTargetsToLocalize.empty()) { -loc.start(); ParamUtils::localizeCallsTo( callTargetsToLocalize, *module, getPassRunner(), [&](Function* func) { markStale(func->name); @@ -592,7 +580,6 @@ loc.start(); loc.stop(); } if (optimize && !worthOptimizing.empty()) { -oai.start(); OptUtils::optimizeAfterInlining(worthOptimizing, module, getPassRunner()); oai.stop(); } From 4640c3a8dde3f4950c3fbf086d45c3f4a86eee49 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:41:13 -0800 Subject: [PATCH 28/44] fix --- src/passes/DeadArgumentElimination.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 6828c1c2613..94ba9d2de6d 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -279,7 +279,6 @@ struct DAE : public Pass { // Scan all the functions. scanner.run(getPassRunner(), module); -scan.stop(); // Combine all the info from the scan. @@ -304,7 +303,6 @@ scan.stop(); } } -combine.stop(); // See comment above, we compute callers once and never again. if (callers.empty()) { @@ -326,7 +324,6 @@ combine.stop(); } } } -callerz.stop(); // Recompute parts of allCalls as necessary. We know which function infos @@ -406,7 +403,6 @@ std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", call } } -allC.stop(); // Track which functions we changed that are worth re-optimizing at the end. std::unordered_set worthOptimizing; @@ -465,13 +461,11 @@ allC.stop(); markStale(func->name); } // Refine return types as well. -ra.stop(); if (refineReturnTypes(func, calls, module)) { refinedReturnTypes = true; markStale(name); markCallersStale(index); } -rr.stop(); auto optimizedIndexes = ParamUtils::applyConstantValues({func}, calls, {}, module); for (auto i : optimizedIndexes) { @@ -479,18 +473,15 @@ rr.stop(); // for that). infoMap[name].unusedParams.insert(i); } -acv.stop(); if (!optimizedIndexes.empty()) { markStale(func->name); } } -opt1.stop(); if (refinedReturnTypes) { // Changing a call expression's return type can propagate out to its // parents, and so we must refinalize. // TODO: We could track in which functions we actually make changes. ReFinalize().run(getPassRunner(), module); -opt2.stop(); } // We now know which parameters are unused, and can potentially remove them. for (Index index = 0; index < numFunctions; index++) { @@ -523,7 +514,6 @@ opt2.stop(); callTargetsToLocalize.insert(name); } } -opt3.stop(); // We can also tell which calls have all their return values dropped. Note // that we can't do this if we changed anything so far, as we may have // modified allCalls (we can't modify a call site twice in one iteration, @@ -571,17 +561,14 @@ opt3.stop(); markCallersStale(index); } } -opt4.stop(); if (!callTargetsToLocalize.empty()) { ParamUtils::localizeCallsTo( callTargetsToLocalize, *module, getPassRunner(), [&](Function* func) { markStale(func->name); }); -loc.stop(); } if (optimize && !worthOptimizing.empty()) { OptUtils::optimizeAfterInlining(worthOptimizing, module, getPassRunner()); -oai.stop(); } return !worthOptimizing.empty() || refinedReturnTypes || From 2d77d4a146cd38fd8a5e46dbad7e0eb0fc1befc5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:41:45 -0800 Subject: [PATCH 29/44] fix --- src/passes/DeadArgumentElimination.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 94ba9d2de6d..abd8ad0e484 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -254,8 +254,6 @@ struct DAE : public Pass { std::vector> callees; bool iteration(Module* module, DAEFunctionInfoMap& infoMap) { - std::cout << "iter\n"; - allDroppedCalls.clear(); #if DAE_DEBUG @@ -273,14 +271,11 @@ struct DAE : public Pass { } #endif - DAEScanner scanner(&infoMap); scanner.walkModuleCode(module); // Scan all the functions. scanner.run(getPassRunner(), module); - - // Combine all the info from the scan. std::vector tailCallees(numFunctions); std::vector hasUnseenCalls(numFunctions); @@ -303,7 +298,6 @@ struct DAE : public Pass { } } - // See comment above, we compute callers once and never again. if (callers.empty()) { // Compute first as sets, to deduplicate. @@ -325,7 +319,6 @@ struct DAE : public Pass { } } - // Recompute parts of allCalls as necessary. We know which function infos // were just updated, and start there: If we updated { A, B }, and A calls // C while B calls C and D, then the list of all calls must be updated for @@ -334,7 +327,6 @@ struct DAE : public Pass { std::unordered_set calledByJustUpdated; for (auto& [func, info] : infoMap) { if (info.justUpdated) { -//std::cerr << "just updated " << func << '\n'; auto index = indexes[func]; justUpdated.insert(index); for (auto& callee : callees[index]) { @@ -345,7 +337,6 @@ struct DAE : public Pass { auto addCallsFrom = [&](Index caller) { auto& info = infoMap[module->functions[caller]->name]; -//std::cerr << "processing calls from " << caller << '\n'; for (auto& [name, calls] : info.calls) { auto& allCallsToName = allCalls[indexes[name]].calls; allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); @@ -357,7 +348,6 @@ struct DAE : public Pass { } }; -std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", called by them=" << calledByJustUpdated.size() << " out of " << numFunctions << '\n'; if (justUpdated.size() + calledByJustUpdated.size() >= numFunctions) { // Many functions need to be processed to do an incremental update. Just // do a full recompute from scratch, which may be faster. @@ -368,13 +358,12 @@ std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", call } } else { // Do an incremental update. - std::cout << " incrememt\n"; // For each such called function, we don't want to alter calls from // unchanged functions. That is, if X calls C and D in the example above, // and X is not just-updated, then X's calls to C and D are fine as they - // are. Leaving such calls alone, remove calls from the callers that we did - // just update, and after that, add them from the fresh data we have on - // those just-updated functions. + // are. Leaving such calls alone, remove calls from the callers that we + // did just update, and after that, add them from the fresh data we have + // on those just-updated functions. for (auto& called : calledByJustUpdated) { auto& calledCalls = allCalls[called]; auto oldSize = calledCalls.calls.size(); @@ -402,7 +391,7 @@ std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", call addCallsFrom(caller); } } - + // Track which functions we changed that are worth re-optimizing at the end. std::unordered_set worthOptimizing; @@ -504,8 +493,7 @@ std::cout << " updating allcalls justUpdated=" << justUpdated.size() << ", call auto [removedIndexes, outcome] = ParamUtils::removeParameters( {func}, infoMap[name].unusedParams, calls, {}, module, getPassRunner()); if (!removedIndexes.empty()) { -//std::cerr << "remove param " << func->name << '\n'; - // Success! + // Success! worthOptimizing.insert(func); markStale(name); markCallersStale(index); From e142546f591d44e3be7eadd0e79d6e27e039f72f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:44:41 -0800 Subject: [PATCH 30/44] fix --- src/passes/DeadArgumentElimination.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index abd8ad0e484..2672943f90e 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -60,7 +60,9 @@ struct DAEFunctionInfo { // Whether this needs to be recomputed. This begins as true for the first // computation, and we reset it every time we touch the function. bool stale = true; - // TODO + // Set after we just updated this. That is the case right after we were stale, + // and were updated to become not stale, and it indicates that we contain new + // data. bool justUpdated = false; // The unused parameters, if any. SortedVector unusedParams; @@ -200,15 +202,15 @@ struct DAE : public Pass { // Map of function names to indexes. This lets us use indexes below for speed. std::unordered_map indexes; - // TODO comment struct CallInfo { - // Store the calls and their origins in parallel vectors, as we need |calls| - // by itself for certain APIs. This is, origins[i] is the function index in + // Store the calls and their origins in parallel vectors (as we need |calls| + // by itself for certain APIs). That is, origins[i] is the function index in // which calls[i] appears. std::vector calls; std::vector origins; }; - // TODO comment + // The set of all calls (and their origins) between functions. We compute this + // incrementally in later iterations, to avoid repeated work. std::vector allCalls; void run(Module* module) override { @@ -250,7 +252,7 @@ struct DAE : public Pass { // of computing this map is significant, so we compute it once at the start // and then use that possibly-over-approximating data. std::vector> callers; - // TODO + // Reverse data: The list of functions called by a function. std::vector> callees; bool iteration(Module* module, DAEFunctionInfoMap& infoMap) { From fe41327608b6db984a6dc291d60979bc6656a714 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:47:27 -0800 Subject: [PATCH 31/44] fix --- src/passes/DeadArgumentElimination.cpp | 150 +++++++++++++------------ 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 2672943f90e..61a14d7dc1e 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -300,7 +300,7 @@ struct DAE : public Pass { } } - // See comment above, we compute callers once and never again. + // See comment above, we compute callers and callees once and never again. if (callers.empty()) { // Compute first as sets, to deduplicate. std::vector> callersSets(numFunctions); @@ -321,78 +321,7 @@ struct DAE : public Pass { } } - // Recompute parts of allCalls as necessary. We know which function infos - // were just updated, and start there: If we updated { A, B }, and A calls - // C while B calls C and D, then the list of all calls must be updated for - // C and D. First, find those functions called by just-updated functions. - std::unordered_set justUpdated; - std::unordered_set calledByJustUpdated; - for (auto& [func, info] : infoMap) { - if (info.justUpdated) { - auto index = indexes[func]; - justUpdated.insert(index); - for (auto& callee : callees[index]) { - calledByJustUpdated.insert(callee); - } - } - } - - auto addCallsFrom = [&](Index caller) { - auto& info = infoMap[module->functions[caller]->name]; - for (auto& [name, calls] : info.calls) { - auto& allCallsToName = allCalls[indexes[name]].calls; - allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); - auto num = calls.size(); - auto& origins = allCalls[indexes[name]].origins; - for (Index i = 0; i < num; i++) { - origins.push_back(caller); - } - } - }; - - if (justUpdated.size() + calledByJustUpdated.size() >= numFunctions) { - // Many functions need to be processed to do an incremental update. Just - // do a full recompute from scratch, which may be faster. - allCalls.clear(); - allCalls.resize(numFunctions); - for (Index caller = 0; caller < numFunctions; caller++) { - addCallsFrom(caller); - } - } else { - // Do an incremental update. - // For each such called function, we don't want to alter calls from - // unchanged functions. That is, if X calls C and D in the example above, - // and X is not just-updated, then X's calls to C and D are fine as they - // are. Leaving such calls alone, remove calls from the callers that we - // did just update, and after that, add them from the fresh data we have - // on those just-updated functions. - for (auto& called : calledByJustUpdated) { - auto& calledCalls = allCalls[called]; - auto oldSize = calledCalls.calls.size(); - assert(oldSize == calledCalls.origins.size()); - Index skip = 0; - for (Index i = 0; i < calledCalls.calls.size(); i++) { - if (justUpdated.count(calledCalls.origins[i])) { - // Remove it by skipping over. - skip++; - } else if (skip) { - // Keep it by writing to the proper place. - calledCalls.calls[i - skip] = calledCalls.calls[i]; - calledCalls.origins[i - skip] = calledCalls.origins[i]; - } - } - if (skip > 0) { - // Update the sizes after removing things. - calledCalls.calls.resize(oldSize - skip); - calledCalls.origins.resize(oldSize - skip); - } - } - // The just-updated callers have been cleaned out of |allCalls|. Add their - // calls, after which that data structure is up-to-date. - for (auto& caller : justUpdated) { - addCallsFrom(caller); - } - } + updateAllCalls(module, infoMap); // Track which functions we changed that are worth re-optimizing at the end. std::unordered_set worthOptimizing; @@ -568,6 +497,81 @@ struct DAE : public Pass { private: std::unordered_map allDroppedCalls; + void updateAllCalls(Module* module, DAEFunctionInfoMap& infoMap) { + // Recompute parts of allCalls as necessary. We know which function infos + // were just updated, and start there: If we updated { A, B }, and A calls + // C while B calls C and D, then the list of all calls must be updated for + // C and D. First, find the functions just updated, and the ones they call. + std::unordered_set justUpdated; + std::unordered_set calledByJustUpdated; + for (auto& [func, info] : infoMap) { + if (info.justUpdated) { + auto index = indexes[func]; + justUpdated.insert(index); + for (auto& callee : callees[index]) { + calledByJustUpdated.insert(callee); + } + } + } + + auto addCallsFrom = [&](Index caller) { + auto& info = infoMap[module->functions[caller]->name]; + for (auto& [name, calls] : info.calls) { + auto& allCallsToName = allCalls[indexes[name]].calls; + allCallsToName.insert(allCallsToName.end(), calls.begin(), calls.end()); + auto num = calls.size(); + auto& origins = allCalls[indexes[name]].origins; + for (Index i = 0; i < num; i++) { + origins.push_back(caller); + } + } + }; + + if (justUpdated.size() + calledByJustUpdated.size() >= numFunctions) { + // Many functions need to be processed to do an incremental update. Just + // do a full recompute from scratch, which may be faster. + allCalls.clear(); + allCalls.resize(numFunctions); + for (Index caller = 0; caller < numFunctions; caller++) { + addCallsFrom(caller); + } + } else { + // Do an incremental update. + // For each such called function, we don't want to alter calls from + // unchanged functions. That is, if X calls C and D in the example above, + // and X is not just-updated, then X's calls to C and D are fine as they + // are. Leaving such calls alone, remove calls from the callers that we + // did just update, and after that, add them from the fresh data we have + // on those just-updated functions. + for (auto& called : calledByJustUpdated) { + auto& calledCalls = allCalls[called]; + auto oldSize = calledCalls.calls.size(); + assert(oldSize == calledCalls.origins.size()); + Index skip = 0; + for (Index i = 0; i < calledCalls.calls.size(); i++) { + if (justUpdated.count(calledCalls.origins[i])) { + // Remove it by skipping over. + skip++; + } else if (skip) { + // Keep it by writing to the proper place. + calledCalls.calls[i - skip] = calledCalls.calls[i]; + calledCalls.origins[i - skip] = calledCalls.origins[i]; + } + } + if (skip > 0) { + // Update the sizes after removing things. + calledCalls.calls.resize(oldSize - skip); + calledCalls.origins.resize(oldSize - skip); + } + } + // The just-updated callers have been cleaned out of |allCalls|. Add their + // calls, after which that data structure is up-to-date. + for (auto& caller : justUpdated) { + addCallsFrom(caller); + } + } + } + // Returns `true` if the caller should be optimized. bool removeReturnValue(Function* func, std::vector& calls, Module* module) { From 5008b438163cc403212da1de10d1ed6db09cb131 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:49:17 -0800 Subject: [PATCH 32/44] fix --- src/passes/DeadArgumentElimination.cpp | 60 +++++++++++++------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 61a14d7dc1e..3447c1ff0df 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -514,6 +514,7 @@ struct DAE : public Pass { } } + // Add calls from one caller to allCalls. auto addCallsFrom = [&](Index caller) { auto& info = infoMap[module->functions[caller]->name]; for (auto& [name, calls] : info.calls) { @@ -535,41 +536,38 @@ struct DAE : public Pass { for (Index caller = 0; caller < numFunctions; caller++) { addCallsFrom(caller); } - } else { - // Do an incremental update. - // For each such called function, we don't want to alter calls from - // unchanged functions. That is, if X calls C and D in the example above, - // and X is not just-updated, then X's calls to C and D are fine as they - // are. Leaving such calls alone, remove calls from the callers that we - // did just update, and after that, add them from the fresh data we have - // on those just-updated functions. - for (auto& called : calledByJustUpdated) { - auto& calledCalls = allCalls[called]; - auto oldSize = calledCalls.calls.size(); - assert(oldSize == calledCalls.origins.size()); - Index skip = 0; - for (Index i = 0; i < calledCalls.calls.size(); i++) { - if (justUpdated.count(calledCalls.origins[i])) { - // Remove it by skipping over. - skip++; - } else if (skip) { - // Keep it by writing to the proper place. - calledCalls.calls[i - skip] = calledCalls.calls[i]; - calledCalls.origins[i - skip] = calledCalls.origins[i]; - } - } - if (skip > 0) { - // Update the sizes after removing things. - calledCalls.calls.resize(oldSize - skip); - calledCalls.origins.resize(oldSize - skip); + return; + } + + // Do an incremental update. First, remove all stale calls from allCalls, + // that is, remove calls from the just-updated functions. + for (auto& called : calledByJustUpdated) { + auto& calledCalls = allCalls[called]; + auto oldSize = calledCalls.calls.size(); + assert(oldSize == calledCalls.origins.size()); + Index skip = 0; + for (Index i = 0; i < calledCalls.calls.size(); i++) { + if (justUpdated.count(calledCalls.origins[i])) { + // Remove it by skipping over. + skip++; + } else if (skip) { + // Keep it by writing to the proper place. + calledCalls.calls[i - skip] = calledCalls.calls[i]; + calledCalls.origins[i - skip] = calledCalls.origins[i]; } } - // The just-updated callers have been cleaned out of |allCalls|. Add their - // calls, after which that data structure is up-to-date. - for (auto& caller : justUpdated) { - addCallsFrom(caller); + if (skip > 0) { + // Update the sizes after removing things. + calledCalls.calls.resize(oldSize - skip); + calledCalls.origins.resize(oldSize - skip); } } + + // The just-updated callers' calls have been cleaned out of allCalls. Add + // them in, leaving us with fully-updated data. + for (auto& caller : justUpdated) { + addCallsFrom(caller); + } } // Returns `true` if the caller should be optimized. From 7418cbc102079edb1ce61c1b683decd9d82f75d7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 14:59:43 -0800 Subject: [PATCH 33/44] nicer --- test/lit/passes/dae-gc.wast | 42 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 2a9fa99cd5f..52154d99a45 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -2,8 +2,6 @@ ;; RUN: foreach %s %t wasm-opt -all --dae -S -o - | filecheck %s (module - ;; CHECK: (type $0 (func)) - ;; CHECK: (type $"{}" (struct)) (type $"{}" (struct)) @@ -70,8 +68,6 @@ ;; Test ref.func and ref.null optimization of constant parameter values. (module - ;; CHECK: (type $0 (func)) - ;; CHECK: (func $foo (type $1) (param $0 (ref (exact $0))) ;; CHECK-NEXT: (local $1 (ref (exact $0))) ;; CHECK-NEXT: (local.set $1 @@ -171,8 +167,6 @@ ;; Test that string constants can be applied. (module - ;; CHECK: (type $0 (func)) - ;; CHECK: (func $0 (type $0) ;; CHECK-NEXT: (call $1) ;; CHECK-NEXT: ) @@ -210,32 +204,32 @@ (module (rec ;; CHECK: (rec - ;; CHECK-NEXT: (type $S (struct)) - (type $S (struct)) - ;; CHECK: (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) - (type $0 (sub (struct (field (mut f64)) (field (mut funcref))))) + ;; CHECK-NEXT: (type $A (struct)) + (type $A (struct)) + ;; CHECK: (type $B (sub (struct (field (mut f64)) (field (mut funcref))))) + (type $B (sub (struct (field (mut f64)) (field (mut funcref))))) ) - ;; CHECK: (func $0 (type $5) (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + ;; CHECK: (func $0 (type $4) (param $0 (ref $B)) (param $1 (ref struct)) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $0 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (func $0 (param $0 (ref $B)) (param $1 (ref struct)) (result f64) (unreachable) ) - ;; CHECK: (func $1 (type $4) (result f64) + ;; CHECK: (func $1 (type $3) (result f64) ;; CHECK-NEXT: (local $0 (ref struct)) - ;; CHECK-NEXT: (local $1 (ref $0)) + ;; CHECK-NEXT: (local $1 (ref $B)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $1 (param $0 (ref $0)) (param $1 (ref struct)) (result f64) + (func $1 (param $0 (ref $B)) (param $1 (ref struct)) (result f64) (unreachable) ) - ;; CHECK: (func $2 (type $1) - ;; CHECK-NEXT: (local $0 (ref (exact $0))) + ;; CHECK: (func $2 (type $0) + ;; CHECK-NEXT: (local $0 (ref (exact $B))) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result f64) ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (struct.new $0 + ;; CHECK-NEXT: (struct.new $B ;; CHECK-NEXT: (call $6) ;; CHECK-NEXT: (ref.func $0) ;; CHECK-NEXT: ) @@ -247,22 +241,22 @@ (func $2 (drop (call $1 - (struct.new $0 + (struct.new $B (call $6) (ref.func $0) ) - (struct.new_default $S) + (struct.new_default $A) ) ) ) - ;; CHECK: (func $4 (type $1) + ;; CHECK: (func $4 (type $0) ;; CHECK-NEXT: (local $0 (ref any)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $4 (param $0 (ref any)) (result f64) (unreachable) ) - ;; CHECK: (func $5 (type $1) + ;; CHECK: (func $5 (type $0) ;; CHECK-NEXT: (call $4) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $6) @@ -271,7 +265,7 @@ (func $5 (drop (call $4 - (struct.new $0 + (struct.new $B (f64.const 0) (ref.func $1) ) @@ -281,7 +275,7 @@ (call $6) ) ) - ;; CHECK: (func $6 (type $4) (result f64) + ;; CHECK: (func $6 (type $3) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $6 (result f64) From d9203ef8f9cc98af7a70e655754ba42e217f374a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:00:30 -0800 Subject: [PATCH 34/44] nicer --- test/lit/passes/dae-gc.wast | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 52154d99a45..29503d4d0d6 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -210,13 +210,11 @@ (type $B (sub (struct (field (mut f64)) (field (mut funcref))))) ) - ;; CHECK: (func $0 (type $4) (param $0 (ref $B)) (param $1 (ref struct)) (result f64) - ;; CHECK-NEXT: (unreachable) + ;; CHECK: (func $nop (type $0) ;; CHECK-NEXT: ) - (func $0 (param $0 (ref $B)) (param $1 (ref struct)) (result f64) - (unreachable) + (func $nop ) - ;; CHECK: (func $1 (type $3) (result f64) + ;; CHECK: (func $1 (type $1) (result f64) ;; CHECK-NEXT: (local $0 (ref struct)) ;; CHECK-NEXT: (local $1 (ref $B)) ;; CHECK-NEXT: (unreachable) @@ -231,7 +229,7 @@ ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (struct.new $B ;; CHECK-NEXT: (call $6) - ;; CHECK-NEXT: (ref.func $0) + ;; CHECK-NEXT: (ref.func $nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $1) @@ -243,7 +241,7 @@ (call $1 (struct.new $B (call $6) - (ref.func $0) + (ref.func $nop) ) (struct.new_default $A) ) @@ -275,7 +273,7 @@ (call $6) ) ) - ;; CHECK: (func $6 (type $3) (result f64) + ;; CHECK: (func $6 (type $1) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $6 (result f64) From 956e778ffc79fbfd8833f2dbf6ea7abce11b59d2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:01:48 -0800 Subject: [PATCH 35/44] nicer --- test/lit/passes/dae-gc.wast | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 29503d4d0d6..ab324f5d173 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -214,27 +214,24 @@ ;; CHECK-NEXT: ) (func $nop ) - ;; CHECK: (func $1 (type $1) (result f64) - ;; CHECK-NEXT: (local $0 (ref struct)) - ;; CHECK-NEXT: (local $1 (ref $B)) + + ;; CHECK: (func $1 (type $0) + ;; CHECK-NEXT: (local $0 (ref $B)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $1 (param $0 (ref $B)) (param $1 (ref struct)) (result f64) + (func $1 (param $0 (ref $B)) (result f64) (unreachable) ) + ;; CHECK: (func $2 (type $0) ;; CHECK-NEXT: (local $0 (ref (exact $B))) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result f64) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (struct.new $B - ;; CHECK-NEXT: (call $6) - ;; CHECK-NEXT: (ref.func $nop) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $1) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (call $6) + ;; CHECK-NEXT: (ref.func $nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $1) ;; CHECK-NEXT: ) (func $2 (drop @@ -243,10 +240,10 @@ (call $6) (ref.func $nop) ) - (struct.new_default $A) ) ) ) + ;; CHECK: (func $4 (type $0) ;; CHECK-NEXT: (local $0 (ref any)) ;; CHECK-NEXT: (unreachable) @@ -254,6 +251,7 @@ (func $4 (param $0 (ref any)) (result f64) (unreachable) ) + ;; CHECK: (func $5 (type $0) ;; CHECK-NEXT: (call $4) ;; CHECK-NEXT: (drop @@ -273,7 +271,8 @@ (call $6) ) ) - ;; CHECK: (func $6 (type $1) (result f64) + + ;; CHECK: (func $6 (type $3) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $6 (result f64) From 006cb6bbac176d1316f3347e9318ac2287e45eb7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:02:55 -0800 Subject: [PATCH 36/44] nicer --- test/lit/passes/dae-gc.wast | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index ab324f5d173..a95985706eb 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -213,13 +213,15 @@ ;; CHECK: (func $nop (type $0) ;; CHECK-NEXT: ) (func $nop + ;; Helper. ) - ;; CHECK: (func $1 (type $0) + ;; CHECK: (func $param (type $0) ;; CHECK-NEXT: (local $0 (ref $B)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $1 (param $0 (ref $B)) (result f64) + (func $param (param $0 (ref $B)) (result f64) + ;; Helper with a param and lets us have calls inside it. (unreachable) ) @@ -231,11 +233,11 @@ ;; CHECK-NEXT: (ref.func $nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $1) + ;; CHECK-NEXT: (call $param) ;; CHECK-NEXT: ) (func $2 (drop - (call $1 + (call $param (struct.new $B (call $6) (ref.func $nop) @@ -263,7 +265,7 @@ (call $4 (struct.new $B (f64.const 0) - (ref.func $1) + (ref.func $param) ) ) ) From f7e565b163d6d8724c8e433dfeaae0d9c89f9018 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:03:11 -0800 Subject: [PATCH 37/44] nicer --- test/lit/passes/dae-gc.wast | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index a95985706eb..118ef3c5c9a 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -220,7 +220,7 @@ ;; CHECK-NEXT: (local $0 (ref $B)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $param (param $0 (ref $B)) (result f64) + (func $param (param $0 (ref $B)) ;; Helper with a param and lets us have calls inside it. (unreachable) ) @@ -236,12 +236,10 @@ ;; CHECK-NEXT: (call $param) ;; CHECK-NEXT: ) (func $2 - (drop - (call $param - (struct.new $B - (call $6) - (ref.func $nop) - ) + (call $param + (struct.new $B + (call $6) + (ref.func $nop) ) ) ) From b905ad79a12b368511d57a36a00ba38561531810 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:03:48 -0800 Subject: [PATCH 38/44] nicer --- test/lit/passes/dae-gc.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 118ef3c5c9a..e7fbdd7746d 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -225,7 +225,7 @@ (unreachable) ) - ;; CHECK: (func $2 (type $0) + ;; CHECK: (func $caller (type $0) ;; CHECK-NEXT: (local $0 (ref (exact $B))) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (struct.new $B @@ -235,7 +235,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $param) ;; CHECK-NEXT: ) - (func $2 + (func $caller (call $param (struct.new $B (call $6) From 56430a0349fc0c43b7979bbca31d3d9596650227 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:04:26 -0800 Subject: [PATCH 39/44] nicer --- test/lit/passes/dae-gc.wast | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index e7fbdd7746d..ea90bbd4f42 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -244,27 +244,23 @@ ) ) - ;; CHECK: (func $4 (type $0) + ;; CHECK: (func $param2 (type $0) ;; CHECK-NEXT: (local $0 (ref any)) - ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $4 (param $0 (ref any)) (result f64) - (unreachable) + (func $param2 (param $0 (ref any)) ) ;; CHECK: (func $5 (type $0) - ;; CHECK-NEXT: (call $4) + ;; CHECK-NEXT: (call $param2) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $6) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $5 - (drop - (call $4 - (struct.new $B - (f64.const 0) - (ref.func $param) - ) + (call $param2 + (struct.new $B + (f64.const 0) + (ref.func $param) ) ) (drop From dfda490cce3baad32c68822b7910d7d1c026fc88 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:04:39 -0800 Subject: [PATCH 40/44] nicer --- test/lit/passes/dae-gc.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index ea90bbd4f42..c538e1f7e3f 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -250,13 +250,13 @@ (func $param2 (param $0 (ref any)) ) - ;; CHECK: (func $5 (type $0) + ;; CHECK: (func $caller2 (type $0) ;; CHECK-NEXT: (call $param2) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $6) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $5 + (func $caller2 (call $param2 (struct.new $B (f64.const 0) From 6e15aa4e78a5e15ac18733f32660d1565afc82ba Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:05:14 -0800 Subject: [PATCH 41/44] nicer --- test/lit/passes/dae-gc.wast | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index c538e1f7e3f..23ba1f0d41e 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -198,8 +198,8 @@ ;; After the first optimization, where we remove params from the call to $1, ;; we update the IR incrementally, and must do so properly: we update $1 and -;; $1's caller $2, and note that $2 also calls $6, so we must update some of -;; $6's callers but not all. +;; $1's caller $2, and note that $2 also calls $result, so we must update some of +;; $result's callers but not all. ;; TODO: pretty names etc. (module (rec @@ -229,7 +229,7 @@ ;; CHECK-NEXT: (local $0 (ref (exact $B))) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (struct.new $B - ;; CHECK-NEXT: (call $6) + ;; CHECK-NEXT: (call $result) ;; CHECK-NEXT: (ref.func $nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -238,7 +238,7 @@ (func $caller (call $param (struct.new $B - (call $6) + (call $result) (ref.func $nop) ) ) @@ -253,7 +253,7 @@ ;; CHECK: (func $caller2 (type $0) ;; CHECK-NEXT: (call $param2) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $6) + ;; CHECK-NEXT: (call $result) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $caller2 @@ -264,14 +264,14 @@ ) ) (drop - (call $6) + (call $result) ) ) - ;; CHECK: (func $6 (type $3) (result f64) + ;; CHECK: (func $result (type $3) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $6 (result f64) + (func $result (result f64) (unreachable) ) ) From a9d46ef3e9ed090a096e0f17b438e2c26d501c84 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:07:18 -0800 Subject: [PATCH 42/44] nicer --- test/lit/passes/dae-gc.wast | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 23ba1f0d41e..aa69448de42 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -197,10 +197,9 @@ ) ;; After the first optimization, where we remove params from the call to $1, -;; we update the IR incrementally, and must do so properly: we update $1 and -;; $1's caller $2, and note that $2 also calls $result, so we must update some of -;; $result's callers but not all. -;; TODO: pretty names etc. +;; we update the IR incrementally, and must do so properly: we update $param and +;; its callers $caller. $caller also calls $result, so we must update some of +;; $result's callers but not all. Ditto with $caller2, $result2. (module (rec ;; CHECK: (rec @@ -263,6 +262,7 @@ (ref.func $param) ) ) + ;; The second call is not nested in this case, to test another form. (drop (call $result) ) From c7708f5f33e7a6fcb9167035487bdda5eb602f67 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:07:45 -0800 Subject: [PATCH 43/44] nicer --- test/lit/passes/dae-gc.wast | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index aa69448de42..30e360bc775 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -202,11 +202,8 @@ ;; $result's callers but not all. Ditto with $caller2, $result2. (module (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $A (struct)) - (type $A (struct)) - ;; CHECK: (type $B (sub (struct (field (mut f64)) (field (mut funcref))))) - (type $B (sub (struct (field (mut f64)) (field (mut funcref))))) + ;; CHECK: (type $A (sub (struct (field (mut f64)) (field (mut funcref))))) + (type $A (sub (struct (field (mut f64)) (field (mut funcref))))) ) ;; CHECK: (func $nop (type $0) @@ -216,18 +213,18 @@ ) ;; CHECK: (func $param (type $0) - ;; CHECK-NEXT: (local $0 (ref $B)) + ;; CHECK-NEXT: (local $0 (ref $A)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $param (param $0 (ref $B)) + (func $param (param $0 (ref $A)) ;; Helper with a param and lets us have calls inside it. (unreachable) ) ;; CHECK: (func $caller (type $0) - ;; CHECK-NEXT: (local $0 (ref (exact $B))) + ;; CHECK-NEXT: (local $0 (ref (exact $A))) ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (struct.new $A ;; CHECK-NEXT: (call $result) ;; CHECK-NEXT: (ref.func $nop) ;; CHECK-NEXT: ) @@ -236,7 +233,7 @@ ;; CHECK-NEXT: ) (func $caller (call $param - (struct.new $B + (struct.new $A (call $result) (ref.func $nop) ) @@ -257,7 +254,7 @@ ;; CHECK-NEXT: ) (func $caller2 (call $param2 - (struct.new $B + (struct.new $A (f64.const 0) (ref.func $param) ) @@ -268,7 +265,7 @@ ) ) - ;; CHECK: (func $result (type $3) (result f64) + ;; CHECK: (func $result (type $2) (result f64) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $result (result f64) From 347e5df9331b6cc2cbda48097fc318f8b989327b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 2 Dec 2025 15:09:07 -0800 Subject: [PATCH 44/44] nicer --- test/lit/passes/dae-gc.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 30e360bc775..4ced51e1d27 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -196,10 +196,10 @@ ) ) -;; After the first optimization, where we remove params from the call to $1, +;; After the first optimization, where we remove params from the call to $param, ;; we update the IR incrementally, and must do so properly: we update $param and ;; its callers $caller. $caller also calls $result, so we must update some of -;; $result's callers but not all. Ditto with $caller2, $result2. +;; $result's callers but not all. Ditto with $caller2. (module (rec ;; CHECK: (type $A (sub (struct (field (mut f64)) (field (mut funcref)))))