diff options
author | Alon Zakai <azakai@google.com> | 2024-08-14 10:45:01 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-14 10:45:01 -0700 |
commit | 39553a0247eb39c2f58c883cd6fb6b7e00f559ad (patch) | |
tree | 13a422451627b4011e2963c1b1e6304fa65421f5 | |
parent | 0c84afe87ea67239c4d7bf885b43b5c4f52322af (diff) | |
download | binaryen-39553a0247eb39c2f58c883cd6fb6b7e00f559ad.tar.gz binaryen-39553a0247eb39c2f58c883cd6fb6b7e00f559ad.tar.bz2 binaryen-39553a0247eb39c2f58c883cd6fb6b7e00f559ad.zip |
Monomorphization: Add a flag to control the required improvement (#6837)
The argument is the minimum benefit we must see for us to decide to optimize, e.g.
--monomorphize --pass-arg=monomorphize-min-benefit@50
When the minimum benefit is 50% then if we reduce the cost by 50% through
monomorphization then we optimize there. 95% would only optimize when we
remove almost all the cost, etc.
In practice I see 95% will actually tend to reduce code size overall, as while we add
monomorphized versions of functions, we only do so when we remove a lot of
work and size, and after inlining we gain benefits. However, 50% or even lower can
lead to better benchmark results, in return for larger code size, just like with
inlining. To be careful, the default is set to 95%.
Previously we optimized whenever we saw any benefit at all, which is the same
as requiring a minimum benefit of 0%. Old tests have the flag applied in this PR
to set that value, so they do not change.
-rwxr-xr-x | scripts/fuzz_opt.py | 5 | ||||
-rw-r--r-- | src/passes/Monomorphize.cpp | 59 | ||||
-rw-r--r-- | test/lit/passes/monomorphize-benefit.wast | 1652 | ||||
-rw-r--r-- | test/lit/passes/monomorphize-consts.wast | 7 | ||||
-rw-r--r-- | test/lit/passes/monomorphize-context.wast | 7 | ||||
-rw-r--r-- | test/lit/passes/monomorphize-drop.wast | 7 | ||||
-rw-r--r-- | test/lit/passes/monomorphize-types.wast | 7 |
7 files changed, 1722 insertions, 22 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index a4adb7b79..4087abed1 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -1566,7 +1566,10 @@ opt_choices = [ ("--memory-packing",), ("--merge-blocks",), ('--merge-locals',), - ('--monomorphize',), + # test a few monomorphization levels, and also -always + ('--monomorphize', '--pass-arg=monomorphize-min-benefit@0'), + ('--monomorphize', '--pass-arg=monomorphize-min-benefit@50'), + ('--monomorphize', '--pass-arg=monomorphize-min-benefit@95'), ('--monomorphize-always',), ('--no-stack-ir',), ('--once-reduction',), diff --git a/src/passes/Monomorphize.cpp b/src/passes/Monomorphize.cpp index f46e27d83..f05d42445 100644 --- a/src/passes/Monomorphize.cpp +++ b/src/passes/Monomorphize.cpp @@ -25,7 +25,7 @@ // parameter. // * If a call provides a constant as a parameter. // * If a call provides a GC allocation as a parameter. TODO -// * If a call is dropped. TODO also other stuff on the outside? +// * If a call is dropped. TODO also other stuff on the outside, e.g. eqz? // // We realize the benefit by creating a monomorphized (specialized/refined) // version of the function, and call that instead. For example, if we have @@ -68,6 +68,26 @@ // testing that always monomorphizes non-trivial callsites, without checking if // the optimizer can help or not (that makes writing testcases simpler). // +// This pass takes an argument: +// +// --pass-arg=monomorphize-min-benefit@N +// +// The minimum amount of benefit we require in order to decide to optimize, +// as a percentage of the original cost. If this is 0 then we always +// optimize, if the cost improves by even 0.0001%. If this is 50 then we +// optimize only when the optimized monomorphized function has half the +// cost of the original, and so forth, that is higher values are more +// careful (and 100 will only optimize when the cost goes to nothing at +// all). +// +// TODO: We may also want more arguments here: +// * Max function size on which to operate (to not even try to optimize huge +// functions, which could be slow). +// * Max optimized size (if the max optimized size is less than the size we +// inline, then all optimized cases would end up inlined; that would also +// put a limit on the size increase). +// * Max absolute size increase (total of all added code). +// // TODO: When we optimize we could run multiple cycles: A calls B calls C might // end up with the refined+optimized B now having refined types in its // call to C, which it did not have before. This is in fact the expected @@ -110,6 +130,9 @@ namespace wasm { namespace { +// Pass arguments. See descriptions in the comment above. +Index MinPercentBenefit = 95; + // A limit on the number of parameters we are willing to have on monomorphized // functions. Large numbers can lead to large stack frames, which can be slow // and lead to stack overflows. @@ -552,6 +575,8 @@ struct Monomorphize : public Pass { void run(Module* module) override { // TODO: parallelize, see comments below + applyArguments(); + // Find all the return-calling functions. We cannot remove their returns // (because turning a return call into a normal call may break the program // by using more stack). @@ -596,6 +621,11 @@ struct Monomorphize : public Pass { } } + void applyArguments() { + MinPercentBenefit = std::stoul(getArgumentOrDefault( + "monomorphize-min-benefit", std::to_string(MinPercentBenefit))); + } + // Try to optimize a call. void processCall(CallInfo& info, Module& wasm) { auto* call = info.call; @@ -678,14 +708,17 @@ struct Monomorphize : public Pass { // keep optimizing from the current contents as we go. It's not // obvious which approach is best here. doOpts(func); - doOpts(monoFunc.get()); // The cost before monomorphization is the old body + the context // operands. The operands will be *removed* from the calling code if we // optimize, and moved into the monomorphized function, so the proper // comparison is the context + the old body, versus the new body (which // includes the reverse-inlined call context). - auto costBefore = CostAnalyzer(func->body).cost; + // + // Note that we use a double here because we are going to subtract and + // multiply this value later (and want to avoid unsigned integer overflow, + // etc.). + double costBefore = CostAnalyzer(func->body).cost; for (auto* operand : context.operands) { // Note that a slight oddity is that we have *not* optimized the // operands before. We optimize func before and after, but the operands @@ -694,13 +727,21 @@ struct Monomorphize : public Pass { // very unoptimized. costBefore += CostAnalyzer(operand).cost; } - auto costAfter = CostAnalyzer(monoFunc->body).cost; - - // TODO: We should probably only accept improvements above some minimum, - // to avoid optimizing cases where we duplicate a huge function but - // only optimize a tiny part of it compared to the original. - if (costAfter >= costBefore) { + if (costBefore == 0) { + // Nothing to optimize away here. (And it would be invalid to divide by + // this amount in the code below.) worthwhile = false; + } else { + // There is a point to optimizing the monomorphized function, do so. + doOpts(monoFunc.get()); + + double costAfter = CostAnalyzer(monoFunc->body).cost; + + // Compute the percentage of benefit we see here. + auto benefit = 100 - ((100 * costAfter) / costBefore); + if (benefit <= MinPercentBenefit) { + worthwhile = false; + } } } diff --git a/test/lit/passes/monomorphize-benefit.wast b/test/lit/passes/monomorphize-benefit.wast new file mode 100644 index 000000000..a1b69fbd2 --- /dev/null +++ b/test/lit/passes/monomorphize-benefit.wast @@ -0,0 +1,1652 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Test optimization decisions while varying the minimum benefit percentage +;; parameter. Zero means any benefit, no matter how small, is worthwhile, while +;; higher values demand more benefit before doing any work. +;; +;; Test with --traps-never-happen (tnh) here so that we optimize more, making it +;; easier to show the effects. This is also more realistic for toolchains like +;; Java/Kotlin/Dart, which is good coverage. + +;; RUN: foreach %s %t wasm-opt --monomorphize -all -tnh -S -o - | filecheck %s --check-prefix DEFAULT +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -tnh -S -o - | filecheck %s --check-prefix ZERO___ +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@33 -all -tnh -S -o - | filecheck %s --check-prefix THIRD__ +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@66 -all -tnh -S -o - | filecheck %s --check-prefix TWOTRDS +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@100 -all -tnh -S -o - | filecheck %s --check-prefix HUNDRED + +(module + (memory 10 20) + + ;; DEFAULT: (type $0 (func (param i32 i32 i32 i32 i32))) + + ;; DEFAULT: (type $1 (func (param i32))) + + ;; DEFAULT: (memory $0 10 20) + + ;; DEFAULT: (func $target (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + ;; DEFAULT-NEXT: (i32.store + ;; DEFAULT-NEXT: (i32.const 10) + ;; DEFAULT-NEXT: (i32.div_s + ;; DEFAULT-NEXT: (local.get $0) + ;; DEFAULT-NEXT: (i32.add + ;; DEFAULT-NEXT: (local.get $0) + ;; DEFAULT-NEXT: (i32.const 1) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (i32.store + ;; DEFAULT-NEXT: (i32.const 20) + ;; DEFAULT-NEXT: (i32.div_s + ;; DEFAULT-NEXT: (local.get $1) + ;; DEFAULT-NEXT: (i32.add + ;; DEFAULT-NEXT: (local.get $1) + ;; DEFAULT-NEXT: (i32.const 1) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (i32.store + ;; DEFAULT-NEXT: (i32.const 30) + ;; DEFAULT-NEXT: (i32.div_s + ;; DEFAULT-NEXT: (local.get $2) + ;; DEFAULT-NEXT: (i32.add + ;; DEFAULT-NEXT: (local.get $2) + ;; DEFAULT-NEXT: (i32.const 1) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (i32.store + ;; DEFAULT-NEXT: (i32.const 40) + ;; DEFAULT-NEXT: (i32.div_s + ;; DEFAULT-NEXT: (local.get $3) + ;; DEFAULT-NEXT: (i32.add + ;; DEFAULT-NEXT: (local.get $3) + ;; DEFAULT-NEXT: (i32.const 1) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (i32.store + ;; DEFAULT-NEXT: (i32.const 50) + ;; DEFAULT-NEXT: (i32.div_s + ;; DEFAULT-NEXT: (local.get $4) + ;; DEFAULT-NEXT: (i32.add + ;; DEFAULT-NEXT: (local.get $4) + ;; DEFAULT-NEXT: (i32.const 1) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (type $0 (func (param i32))) + + ;; ZERO___: (type $1 (func (param i32 i32 i32 i32 i32))) + + ;; ZERO___: (type $2 (func)) + + ;; ZERO___: (type $3 (func (param i32 i32))) + + ;; ZERO___: (type $4 (func (param i32 i32 i32))) + + ;; ZERO___: (type $5 (func (param i32 i32 i32 i32))) + + ;; ZERO___: (memory $0 10 20) + + ;; ZERO___: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + ;; ZERO___-NEXT: (i32.store + ;; ZERO___-NEXT: (i32.const 10) + ;; ZERO___-NEXT: (i32.div_s + ;; ZERO___-NEXT: (local.get $0) + ;; ZERO___-NEXT: (i32.add + ;; ZERO___-NEXT: (local.get $0) + ;; ZERO___-NEXT: (i32.const 1) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (i32.store + ;; ZERO___-NEXT: (i32.const 20) + ;; ZERO___-NEXT: (i32.div_s + ;; ZERO___-NEXT: (local.get $1) + ;; ZERO___-NEXT: (i32.add + ;; ZERO___-NEXT: (local.get $1) + ;; ZERO___-NEXT: (i32.const 1) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (i32.store + ;; ZERO___-NEXT: (i32.const 30) + ;; ZERO___-NEXT: (i32.div_s + ;; ZERO___-NEXT: (local.get $2) + ;; ZERO___-NEXT: (i32.add + ;; ZERO___-NEXT: (local.get $2) + ;; ZERO___-NEXT: (i32.const 1) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (i32.store + ;; ZERO___-NEXT: (i32.const 40) + ;; ZERO___-NEXT: (i32.div_s + ;; ZERO___-NEXT: (local.get $3) + ;; ZERO___-NEXT: (i32.add + ;; ZERO___-NEXT: (local.get $3) + ;; ZERO___-NEXT: (i32.const 1) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (i32.store + ;; ZERO___-NEXT: (i32.const 50) + ;; ZERO___-NEXT: (i32.div_s + ;; ZERO___-NEXT: (local.get $4) + ;; ZERO___-NEXT: (i32.add + ;; ZERO___-NEXT: (local.get $4) + ;; ZERO___-NEXT: (i32.const 1) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; THIRD__: (type $0 (func (param i32))) + + ;; THIRD__: (type $1 (func (param i32 i32 i32 i32 i32))) + + ;; THIRD__: (type $2 (func)) + + ;; THIRD__: (type $3 (func (param i32 i32))) + + ;; THIRD__: (memory $0 10 20) + + ;; THIRD__: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + ;; THIRD__-NEXT: (i32.store + ;; THIRD__-NEXT: (i32.const 10) + ;; THIRD__-NEXT: (i32.div_s + ;; THIRD__-NEXT: (local.get $0) + ;; THIRD__-NEXT: (i32.add + ;; THIRD__-NEXT: (local.get $0) + ;; THIRD__-NEXT: (i32.const 1) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (i32.store + ;; THIRD__-NEXT: (i32.const 20) + ;; THIRD__-NEXT: (i32.div_s + ;; THIRD__-NEXT: (local.get $1) + ;; THIRD__-NEXT: (i32.add + ;; THIRD__-NEXT: (local.get $1) + ;; THIRD__-NEXT: (i32.const 1) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (i32.store + ;; THIRD__-NEXT: (i32.const 30) + ;; THIRD__-NEXT: (i32.div_s + ;; THIRD__-NEXT: (local.get $2) + ;; THIRD__-NEXT: (i32.add + ;; THIRD__-NEXT: (local.get $2) + ;; THIRD__-NEXT: (i32.const 1) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (i32.store + ;; THIRD__-NEXT: (i32.const 40) + ;; THIRD__-NEXT: (i32.div_s + ;; THIRD__-NEXT: (local.get $3) + ;; THIRD__-NEXT: (i32.add + ;; THIRD__-NEXT: (local.get $3) + ;; THIRD__-NEXT: (i32.const 1) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (i32.store + ;; THIRD__-NEXT: (i32.const 50) + ;; THIRD__-NEXT: (i32.div_s + ;; THIRD__-NEXT: (local.get $4) + ;; THIRD__-NEXT: (i32.add + ;; THIRD__-NEXT: (local.get $4) + ;; THIRD__-NEXT: (i32.const 1) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (type $0 (func (param i32 i32 i32 i32 i32))) + + ;; TWOTRDS: (type $1 (func (param i32))) + + ;; TWOTRDS: (type $2 (func)) + + ;; TWOTRDS: (memory $0 10 20) + + ;; TWOTRDS: (func $target (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + ;; TWOTRDS-NEXT: (i32.store + ;; TWOTRDS-NEXT: (i32.const 10) + ;; TWOTRDS-NEXT: (i32.div_s + ;; TWOTRDS-NEXT: (local.get $0) + ;; TWOTRDS-NEXT: (i32.add + ;; TWOTRDS-NEXT: (local.get $0) + ;; TWOTRDS-NEXT: (i32.const 1) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (i32.store + ;; TWOTRDS-NEXT: (i32.const 20) + ;; TWOTRDS-NEXT: (i32.div_s + ;; TWOTRDS-NEXT: (local.get $1) + ;; TWOTRDS-NEXT: (i32.add + ;; TWOTRDS-NEXT: (local.get $1) + ;; TWOTRDS-NEXT: (i32.const 1) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (i32.store + ;; TWOTRDS-NEXT: (i32.const 30) + ;; TWOTRDS-NEXT: (i32.div_s + ;; TWOTRDS-NEXT: (local.get $2) + ;; TWOTRDS-NEXT: (i32.add + ;; TWOTRDS-NEXT: (local.get $2) + ;; TWOTRDS-NEXT: (i32.const 1) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (i32.store + ;; TWOTRDS-NEXT: (i32.const 40) + ;; TWOTRDS-NEXT: (i32.div_s + ;; TWOTRDS-NEXT: (local.get $3) + ;; TWOTRDS-NEXT: (i32.add + ;; TWOTRDS-NEXT: (local.get $3) + ;; TWOTRDS-NEXT: (i32.const 1) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (i32.store + ;; TWOTRDS-NEXT: (i32.const 50) + ;; TWOTRDS-NEXT: (i32.div_s + ;; TWOTRDS-NEXT: (local.get $4) + ;; TWOTRDS-NEXT: (i32.add + ;; TWOTRDS-NEXT: (local.get $4) + ;; TWOTRDS-NEXT: (i32.const 1) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (type $0 (func (param i32 i32 i32 i32 i32))) + + ;; HUNDRED: (type $1 (func (param i32))) + + ;; HUNDRED: (memory $0 10 20) + + ;; HUNDRED: (func $target (type $0) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) + ;; HUNDRED-NEXT: (i32.store + ;; HUNDRED-NEXT: (i32.const 10) + ;; HUNDRED-NEXT: (i32.div_s + ;; HUNDRED-NEXT: (local.get $0) + ;; HUNDRED-NEXT: (i32.add + ;; HUNDRED-NEXT: (local.get $0) + ;; HUNDRED-NEXT: (i32.const 1) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (i32.store + ;; HUNDRED-NEXT: (i32.const 20) + ;; HUNDRED-NEXT: (i32.div_s + ;; HUNDRED-NEXT: (local.get $1) + ;; HUNDRED-NEXT: (i32.add + ;; HUNDRED-NEXT: (local.get $1) + ;; HUNDRED-NEXT: (i32.const 1) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (i32.store + ;; HUNDRED-NEXT: (i32.const 30) + ;; HUNDRED-NEXT: (i32.div_s + ;; HUNDRED-NEXT: (local.get $2) + ;; HUNDRED-NEXT: (i32.add + ;; HUNDRED-NEXT: (local.get $2) + ;; HUNDRED-NEXT: (i32.const 1) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (i32.store + ;; HUNDRED-NEXT: (i32.const 40) + ;; HUNDRED-NEXT: (i32.div_s + ;; HUNDRED-NEXT: (local.get $3) + ;; HUNDRED-NEXT: (i32.add + ;; HUNDRED-NEXT: (local.get $3) + ;; HUNDRED-NEXT: (i32.const 1) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (i32.store + ;; HUNDRED-NEXT: (i32.const 50) + ;; HUNDRED-NEXT: (i32.div_s + ;; HUNDRED-NEXT: (local.get $4) + ;; HUNDRED-NEXT: (i32.add + ;; HUNDRED-NEXT: (local.get $4) + ;; HUNDRED-NEXT: (i32.const 1) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $target (param $a i32) (param $b i32) (param $c i32) (param $d i32) (param $e i32) + ;; This function takes five parameters and uses each one to do some work. In + ;; Each of the following identical stores, when we know one of the five + ;; params, we can compute in full the value stored. (The store offsets + ;; differ to guard against a future dead store elimination.) + (i32.store + (i32.const 10) + (i32.div_s + (local.get $a) + (i32.add + (local.get $a) + (i32.const 1) + ) + ) + ) + (i32.store + (i32.const 20) + (i32.div_s + (local.get $b) + (i32.add + (local.get $b) + (i32.const 1) + ) + ) + ) + (i32.store + (i32.const 30) + (i32.div_s + (local.get $c) + (i32.add + (local.get $c) + (i32.const 1) + ) + ) + ) + (i32.store + (i32.const 40) + (i32.div_s + (local.get $d) + (i32.add + (local.get $d) + (i32.const 1) + ) + ) + ) + (i32.store + (i32.const 50) + (i32.div_s + (local.get $e) + (i32.add + (local.get $e) + (i32.const 1) + ) + ) + ) + ) + + ;; DEFAULT: (func $calls (type $1) (param $x i32) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (func $calls (type $0) (param $x i32) + ;; ZERO___-NEXT: (call $target_2) + ;; ZERO___-NEXT: (call $target_3 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target_4 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target_5 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target_6 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; THIRD__: (func $calls (type $0) (param $x i32) + ;; THIRD__-NEXT: (call $target_2) + ;; THIRD__-NEXT: (call $target_3 + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target_4 + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (i32.const 42) + ;; THIRD__-NEXT: (i32.const 42) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (i32.const 42) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (func $calls (type $1) (param $x i32) + ;; TWOTRDS-NEXT: (call $target_2) + ;; TWOTRDS-NEXT: (call $target + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (func $calls (type $1) (param $x i32) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $target + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $calls (param $x i32) + ;; Call the target with an increasing amount of non-constant params, 0-5. + ;; + ;; With 5 unknowns, the call context is trivial and we do nothing. All the + ;; differences are therefore on 0-4: + ;; + ;; * ZERO monomorphizes all of 0-4. + ;; * THIRD monomorphizes only 0-2. + ;; * TWOTRDS monomorphizes just 0. + ;; * HUNDRED monomorphizes none at all. + + (call $target + (i32.const 42) + (i32.const 42) + (i32.const 42) + (i32.const 42) + (i32.const 42) + ) + (call $target + (local.get $x) + (i32.const 42) + (i32.const 42) + (i32.const 42) + (i32.const 42) + ) + (call $target + (local.get $x) + (local.get $x) + (i32.const 42) + (i32.const 42) + (i32.const 42) + ) + (call $target + (local.get $x) + (local.get $x) + (local.get $x) + (i32.const 42) + (i32.const 42) + ) + (call $target + (local.get $x) + (local.get $x) + (local.get $x) + (local.get $x) + (i32.const 42) + ) + (call $target + (local.get $x) + (local.get $x) + (local.get $x) + (local.get $x) + (local.get $x) + ) + ) +) + +;; ZERO___: (func $target_2 (type $2) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 10) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 20) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 30) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 40) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 50) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target_3 (type $0) (param $0 i32) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 10) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 20) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 30) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 40) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 50) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target_4 (type $3) (param $0 i32) (param $1 i32) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 10) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 20) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 30) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 40) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 50) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target_5 (type $4) (param $0 i32) (param $1 i32) (param $2 i32) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 10) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 20) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 30) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $2) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $2) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 40) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 50) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target_6 (type $5) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 10) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 20) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 30) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $2) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $2) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 40) +;; ZERO___-NEXT: (i32.div_s +;; ZERO___-NEXT: (local.get $3) +;; ZERO___-NEXT: (i32.add +;; ZERO___-NEXT: (local.get $3) +;; ZERO___-NEXT: (i32.const 1) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (i32.store +;; ZERO___-NEXT: (i32.const 50) +;; ZERO___-NEXT: (i32.const 0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; THIRD__: (func $target_2 (type $2) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 10) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 20) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 30) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 40) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 50) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target_3 (type $0) (param $0 i32) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 10) +;; THIRD__-NEXT: (i32.div_s +;; THIRD__-NEXT: (local.get $0) +;; THIRD__-NEXT: (i32.add +;; THIRD__-NEXT: (local.get $0) +;; THIRD__-NEXT: (i32.const 1) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 20) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 30) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 40) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 50) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target_4 (type $3) (param $0 i32) (param $1 i32) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 10) +;; THIRD__-NEXT: (i32.div_s +;; THIRD__-NEXT: (local.get $0) +;; THIRD__-NEXT: (i32.add +;; THIRD__-NEXT: (local.get $0) +;; THIRD__-NEXT: (i32.const 1) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 20) +;; THIRD__-NEXT: (i32.div_s +;; THIRD__-NEXT: (local.get $1) +;; THIRD__-NEXT: (i32.add +;; THIRD__-NEXT: (local.get $1) +;; THIRD__-NEXT: (i32.const 1) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 30) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 40) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: (i32.store +;; THIRD__-NEXT: (i32.const 50) +;; THIRD__-NEXT: (i32.const 0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) + +;; TWOTRDS: (func $target_2 (type $2) +;; TWOTRDS-NEXT: (i32.store +;; TWOTRDS-NEXT: (i32.const 10) +;; TWOTRDS-NEXT: (i32.const 0) +;; TWOTRDS-NEXT: ) +;; TWOTRDS-NEXT: (i32.store +;; TWOTRDS-NEXT: (i32.const 20) +;; TWOTRDS-NEXT: (i32.const 0) +;; TWOTRDS-NEXT: ) +;; TWOTRDS-NEXT: (i32.store +;; TWOTRDS-NEXT: (i32.const 30) +;; TWOTRDS-NEXT: (i32.const 0) +;; TWOTRDS-NEXT: ) +;; TWOTRDS-NEXT: (i32.store +;; TWOTRDS-NEXT: (i32.const 40) +;; TWOTRDS-NEXT: (i32.const 0) +;; TWOTRDS-NEXT: ) +;; TWOTRDS-NEXT: (i32.store +;; TWOTRDS-NEXT: (i32.const 50) +;; TWOTRDS-NEXT: (i32.const 0) +;; TWOTRDS-NEXT: ) +;; TWOTRDS-NEXT: ) +(module + ;; DEFAULT: (type $A (sub (struct (field i32)))) + ;; ZERO___: (type $A (sub (struct (field i32)))) + ;; THIRD__: (type $A (sub (struct (field i32)))) + ;; TWOTRDS: (type $A (sub (struct (field i32)))) + ;; HUNDRED: (type $A (sub (struct (field i32)))) + (type $A (sub (struct (field i32)))) + + ;; DEFAULT: (type $1 (func)) + + ;; DEFAULT: (type $2 (func (param anyref) (result (ref $A)))) + + ;; DEFAULT: (type $3 (func (param anyref i32))) + + ;; DEFAULT: (type $4 (func (param (ref $A)))) + + ;; DEFAULT: (type $5 (func (param anyref))) + + ;; DEFAULT: (type $6 (func (param i32))) + + ;; DEFAULT: (import "a" "b" (func $import (type $1))) + ;; ZERO___: (type $1 (func)) + + ;; ZERO___: (type $2 (func (param anyref) (result (ref $A)))) + + ;; ZERO___: (type $3 (func (param anyref i32))) + + ;; ZERO___: (type $4 (func (param anyref))) + + ;; ZERO___: (type $5 (func (param i32) (result (ref $A)))) + + ;; ZERO___: (type $6 (func (param i32))) + + ;; ZERO___: (type $7 (func (result (ref $A)))) + + ;; ZERO___: (type $8 (func (param (ref $A)))) + + ;; ZERO___: (import "a" "b" (func $import (type $1))) + ;; THIRD__: (type $1 (func)) + + ;; THIRD__: (type $2 (func (param anyref) (result (ref $A)))) + + ;; THIRD__: (type $3 (func (param anyref i32))) + + ;; THIRD__: (type $4 (func (param (ref $A)))) + + ;; THIRD__: (type $5 (func (param anyref))) + + ;; THIRD__: (type $6 (func (param i32) (result (ref $A)))) + + ;; THIRD__: (type $7 (func (param i32))) + + ;; THIRD__: (type $8 (func (result (ref $A)))) + + ;; THIRD__: (import "a" "b" (func $import (type $1))) + ;; TWOTRDS: (type $1 (func)) + + ;; TWOTRDS: (type $2 (func (param anyref) (result (ref $A)))) + + ;; TWOTRDS: (type $3 (func (param anyref i32))) + + ;; TWOTRDS: (type $4 (func (param (ref $A)))) + + ;; TWOTRDS: (type $5 (func (param anyref))) + + ;; TWOTRDS: (type $6 (func (param i32))) + + ;; TWOTRDS: (import "a" "b" (func $import (type $1))) + ;; HUNDRED: (type $1 (func (param anyref) (result (ref $A)))) + + ;; HUNDRED: (type $2 (func (param anyref i32))) + + ;; HUNDRED: (type $3 (func)) + + ;; HUNDRED: (type $4 (func (param (ref $A)))) + + ;; HUNDRED: (import "a" "b" (func $import (type $3))) + (import "a" "b" (func $import)) + + ;; DEFAULT: (import "a" "c" (func $import2 (type $4) (param (ref $A)))) + ;; ZERO___: (import "a" "c" (func $import2 (type $8) (param (ref $A)))) + ;; THIRD__: (import "a" "c" (func $import2 (type $4) (param (ref $A)))) + ;; TWOTRDS: (import "a" "c" (func $import2 (type $4) (param (ref $A)))) + ;; HUNDRED: (import "a" "c" (func $import2 (type $4) (param (ref $A)))) + (import "a" "c" (func $import2 (param (ref $A)))) + + ;; DEFAULT: (func $target-long (type $2) (param $0 anyref) (result (ref $A)) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (call $import) + ;; DEFAULT-NEXT: (ref.cast (ref $A) + ;; DEFAULT-NEXT: (local.get $0) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (func $target-long (type $2) (param $0 anyref) (result (ref $A)) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (call $import) + ;; ZERO___-NEXT: (ref.cast (ref $A) + ;; ZERO___-NEXT: (local.get $0) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; THIRD__: (func $target-long (type $2) (param $0 anyref) (result (ref $A)) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (call $import) + ;; THIRD__-NEXT: (ref.cast (ref $A) + ;; THIRD__-NEXT: (local.get $0) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (func $target-long (type $2) (param $0 anyref) (result (ref $A)) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (call $import) + ;; TWOTRDS-NEXT: (ref.cast (ref $A) + ;; TWOTRDS-NEXT: (local.get $0) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (func $target-long (type $1) (param $0 anyref) (result (ref $A)) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (call $import) + ;; HUNDRED-NEXT: (ref.cast (ref $A) + ;; HUNDRED-NEXT: (local.get $0) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $target-long (param $any anyref) (result (ref $A)) + ;; This function does a cast, aside from a lot of other work. The other work + ;; causes us to only optimize when we are ok with getting a very small % of + ;; improvement. + (call $import) + (call $import) + (call $import) + (call $import) + (call $import) + (call $import) + (ref.cast (ref $A) + (local.get $any) + ) + ) + + ;; DEFAULT: (func $target-short (type $2) (param $0 anyref) (result (ref $A)) + ;; DEFAULT-NEXT: (ref.cast (ref $A) + ;; DEFAULT-NEXT: (local.get $0) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (func $target-short (type $2) (param $0 anyref) (result (ref $A)) + ;; ZERO___-NEXT: (ref.cast (ref $A) + ;; ZERO___-NEXT: (local.get $0) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; THIRD__: (func $target-short (type $2) (param $0 anyref) (result (ref $A)) + ;; THIRD__-NEXT: (ref.cast (ref $A) + ;; THIRD__-NEXT: (local.get $0) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (func $target-short (type $2) (param $0 anyref) (result (ref $A)) + ;; TWOTRDS-NEXT: (ref.cast (ref $A) + ;; TWOTRDS-NEXT: (local.get $0) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (func $target-short (type $1) (param $0 anyref) (result (ref $A)) + ;; HUNDRED-NEXT: (ref.cast (ref $A) + ;; HUNDRED-NEXT: (local.get $0) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $target-short (param $any anyref) (result (ref $A)) + ;; As above, but without all the work in the middle: this is really just a + ;; simple casting function, and we can remove almost all the work here when + ;; we remove the cast, meaning we optimize in more cases. + (ref.cast (ref $A) + (local.get $any) + ) + ) + + ;; DEFAULT: (func $calls-long (type $3) (param $x anyref) (param $y i32) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (drop + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (local.get $y) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (drop + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (local.get $y) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (drop + ;; DEFAULT-NEXT: (call $target-long + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (func $calls-long (type $3) (param $x anyref) (param $y i32) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-long + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-long_6 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-long_7 + ;; ZERO___-NEXT: (local.get $y) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-long_8 + ;; ZERO___-NEXT: (local.get $y) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-long_9) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-long_10) + ;; ZERO___-NEXT: ) + ;; THIRD__: (func $calls-long (type $3) (param $x anyref) (param $y i32) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-long + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (drop + ;; THIRD__-NEXT: (call $target-long + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-long + ;; THIRD__-NEXT: (struct.new $A + ;; THIRD__-NEXT: (local.get $y) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (drop + ;; THIRD__-NEXT: (call $target-long + ;; THIRD__-NEXT: (struct.new $A + ;; THIRD__-NEXT: (local.get $y) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-long + ;; THIRD__-NEXT: (struct.new $A + ;; THIRD__-NEXT: (i32.const 42) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target-long_6) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (func $calls-long (type $3) (param $x anyref) (param $y i32) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (drop + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (local.get $y) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (drop + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (local.get $y) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (drop + ;; TWOTRDS-NEXT: (call $target-long + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (func $calls-long (type $2) (param $x anyref) (param $y i32) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (local.get $y) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (local.get $y) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-long + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $calls-long (param $x anyref) (param $y i32) + ;; Various calls to $target-long. Because of the large amount of work that + ;; cannot be removed there, all we do here is: + ;; * Optimize in all cases when the minimum benefit is 0% (except the + ;; first call, which is a trivial call context). Removing a cast is + ;; enough to justify optimizing. + ;; * In 33% we optimize only the very last case. There we remove both a + ;; cast and a struct.new, which ends up just over 33%. + ;; * In 66% and 100% we optimize nothing at all. + + ;; Call with an unknown input and the output is sent to an import. + (call $import2 + (call $target-long + (local.get $x) + ) + ) + ;; Ditto, but drop the output. + (drop + (call $target-long + (local.get $x) + ) + ) + ;; Calls with a struct.new input. + (call $import2 + (call $target-long + (struct.new $A + (local.get $y) + ) + ) + ) + (drop + (call $target-long + (struct.new $A + (local.get $y) + ) + ) + ) + ;; Now the struct.new has a constant input. + (call $import2 + (call $target-long + (struct.new $A + (i32.const 42) + ) + ) + ) + (drop + (call $target-long + (struct.new $A + (i32.const 42) + ) + ) + ) + ) + + ;; DEFAULT: (func $calls-short (type $3) (param $x anyref) (param $y i32) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-short + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target-short_6 + ;; DEFAULT-NEXT: (local.get $x) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-short + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (local.get $y) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target-short_7 + ;; DEFAULT-NEXT: (local.get $y) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $import2 + ;; DEFAULT-NEXT: (call $target-short + ;; DEFAULT-NEXT: (struct.new $A + ;; DEFAULT-NEXT: (i32.const 42) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: ) + ;; DEFAULT-NEXT: (call $target-short_8) + ;; DEFAULT-NEXT: ) + ;; ZERO___: (func $calls-short (type $3) (param $x anyref) (param $y i32) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-short + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-short_11 + ;; ZERO___-NEXT: (local.get $x) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-short_12 + ;; ZERO___-NEXT: (local.get $y) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-short_13 + ;; ZERO___-NEXT: (local.get $y) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $import2 + ;; ZERO___-NEXT: (call $target-short_14) + ;; ZERO___-NEXT: ) + ;; ZERO___-NEXT: (call $target-short_15) + ;; ZERO___-NEXT: ) + ;; THIRD__: (func $calls-short (type $3) (param $x anyref) (param $y i32) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-short + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target-short_7 + ;; THIRD__-NEXT: (local.get $x) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-short_8 + ;; THIRD__-NEXT: (local.get $y) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target-short_9 + ;; THIRD__-NEXT: (local.get $y) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $import2 + ;; THIRD__-NEXT: (call $target-short_10) + ;; THIRD__-NEXT: ) + ;; THIRD__-NEXT: (call $target-short_11) + ;; THIRD__-NEXT: ) + ;; TWOTRDS: (func $calls-short (type $3) (param $x anyref) (param $y i32) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-short + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target-short_6 + ;; TWOTRDS-NEXT: (local.get $x) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-short + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (local.get $y) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target-short_7 + ;; TWOTRDS-NEXT: (local.get $y) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $import2 + ;; TWOTRDS-NEXT: (call $target-short + ;; TWOTRDS-NEXT: (struct.new $A + ;; TWOTRDS-NEXT: (i32.const 42) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: ) + ;; TWOTRDS-NEXT: (call $target-short_8) + ;; TWOTRDS-NEXT: ) + ;; HUNDRED: (func $calls-short (type $2) (param $x anyref) (param $y i32) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (local.get $x) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (local.get $y) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (local.get $y) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (call $import2 + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: (drop + ;; HUNDRED-NEXT: (call $target-short + ;; HUNDRED-NEXT: (struct.new $A + ;; HUNDRED-NEXT: (i32.const 42) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + ;; HUNDRED-NEXT: ) + (func $calls-short (param $x anyref) (param $y i32) + ;; As above, but now calling the short function: + ;; * 0% is the same with the long function: any improvement is enough. + ;; * 33% optimizes them all (but for the first, which is a trivial call + ;; context). + ;; * 66% optimizes a few less cases: when the output isn't dropped then we + ;; can't do enough work to justify it. + ;; * 100% optimizes nothing. + (call $import2 + (call $target-short + (local.get $x) + ) + ) + (drop + (call $target-short + (local.get $x) + ) + ) + (call $import2 + (call $target-short + (struct.new $A + (local.get $y) + ) + ) + ) + (drop + (call $target-short + (struct.new $A + (local.get $y) + ) + ) + ) + (call $import2 + (call $target-short + (struct.new $A + (i32.const 42) + ) + ) + ) + (drop + (call $target-short + (struct.new $A + (i32.const 42) + ) + ) + ) + ) +) +;; DEFAULT: (func $target-short_6 (type $5) (param $0 anyref) +;; DEFAULT-NEXT: (nop) +;; DEFAULT-NEXT: ) + +;; DEFAULT: (func $target-short_7 (type $6) (param $0 i32) +;; DEFAULT-NEXT: (nop) +;; DEFAULT-NEXT: ) + +;; DEFAULT: (func $target-short_8 (type $1) +;; DEFAULT-NEXT: (nop) +;; DEFAULT-NEXT: ) + +;; ZERO___: (func $target-long_6 (type $4) (param $0 anyref) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-long_7 (type $5) (param $0 i32) (result (ref $A)) +;; ZERO___-NEXT: (local $1 (ref $A)) +;; ZERO___-NEXT: (local.set $1 +;; ZERO___-NEXT: (struct.new $A +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (local.get $1) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-long_8 (type $6) (param $0 i32) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-long_9 (type $7) (result (ref $A)) +;; ZERO___-NEXT: (local $0 (ref $A)) +;; ZERO___-NEXT: (local.set $0 +;; ZERO___-NEXT: (struct.new $A +;; ZERO___-NEXT: (i32.const 42) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-long_10 (type $1) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: (call $import) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-short_11 (type $4) (param $0 anyref) +;; ZERO___-NEXT: (nop) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-short_12 (type $5) (param $0 i32) (result (ref $A)) +;; ZERO___-NEXT: (struct.new $A +;; ZERO___-NEXT: (local.get $0) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-short_13 (type $6) (param $0 i32) +;; ZERO___-NEXT: (nop) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-short_14 (type $7) (result (ref $A)) +;; ZERO___-NEXT: (struct.new $A +;; ZERO___-NEXT: (i32.const 42) +;; ZERO___-NEXT: ) +;; ZERO___-NEXT: ) + +;; ZERO___: (func $target-short_15 (type $1) +;; ZERO___-NEXT: (nop) +;; ZERO___-NEXT: ) + +;; THIRD__: (func $target-long_6 (type $1) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: (call $import) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target-short_7 (type $5) (param $0 anyref) +;; THIRD__-NEXT: (nop) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target-short_8 (type $6) (param $0 i32) (result (ref $A)) +;; THIRD__-NEXT: (struct.new $A +;; THIRD__-NEXT: (local.get $0) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target-short_9 (type $7) (param $0 i32) +;; THIRD__-NEXT: (nop) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target-short_10 (type $8) (result (ref $A)) +;; THIRD__-NEXT: (struct.new $A +;; THIRD__-NEXT: (i32.const 42) +;; THIRD__-NEXT: ) +;; THIRD__-NEXT: ) + +;; THIRD__: (func $target-short_11 (type $1) +;; THIRD__-NEXT: (nop) +;; THIRD__-NEXT: ) + +;; TWOTRDS: (func $target-short_6 (type $5) (param $0 anyref) +;; TWOTRDS-NEXT: (nop) +;; TWOTRDS-NEXT: ) + +;; TWOTRDS: (func $target-short_7 (type $6) (param $0 i32) +;; TWOTRDS-NEXT: (nop) +;; TWOTRDS-NEXT: ) + +;; TWOTRDS: (func $target-short_8 (type $1) +;; TWOTRDS-NEXT: (nop) +;; TWOTRDS-NEXT: ) diff --git a/test/lit/passes/monomorphize-consts.wast b/test/lit/passes/monomorphize-consts.wast index d71367569..1d18f6dab 100644 --- a/test/lit/passes/monomorphize-consts.wast +++ b/test/lit/passes/monomorphize-consts.wast @@ -2,10 +2,11 @@ ;; As in monomorphize-types.wast, test in both "always" mode, which always ;; monomorphizes, and in "careful" mode which does it only when it appears to -;; actually help. +;; actually help, and use a minimum benefit of 0 to make it easy to write +;; small testcases. -;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS -;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL +;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -S -o - | filecheck %s --check-prefix CAREFUL (module ;; Test that constants are monomorphized. diff --git a/test/lit/passes/monomorphize-context.wast b/test/lit/passes/monomorphize-context.wast index d3bc4a242..e4391d3a5 100644 --- a/test/lit/passes/monomorphize-context.wast +++ b/test/lit/passes/monomorphize-context.wast @@ -2,10 +2,11 @@ ;; As in monomorphize-types.wast, test in both "always" mode, which always ;; monomorphizes, and in "careful" mode which does it only when it appears to -;; actually help. +;; actually help, and use a minimum benefit of 0 to make it easy to write +;; small testcases. -;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS -;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL +;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -S -o - | filecheck %s --check-prefix CAREFUL (module ;; ALWAYS: (type $0 (func (param i32) (result i32))) diff --git a/test/lit/passes/monomorphize-drop.wast b/test/lit/passes/monomorphize-drop.wast index 63923f768..a9f0e1f06 100644 --- a/test/lit/passes/monomorphize-drop.wast +++ b/test/lit/passes/monomorphize-drop.wast @@ -2,10 +2,11 @@ ;; As in monomorphize-types.wast, test in both "always" mode, which always ;; monomorphizes, and in "careful" mode which does it only when it appears to -;; actually help. +;; actually help, and use a minimum benefit of 0 to make it easy to write +;; small testcases. -;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS -;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL +;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -S -o - | filecheck %s --check-prefix CAREFUL (module ;; Test that dropped functions are monomorphized, and the drop is reverse- diff --git a/test/lit/passes/monomorphize-types.wast b/test/lit/passes/monomorphize-types.wast index 7d90a0f87..863ccc086 100644 --- a/test/lit/passes/monomorphize-types.wast +++ b/test/lit/passes/monomorphize-types.wast @@ -1,10 +1,11 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; Test in both "always" mode, which always monomorphizes, and in "careful" -;; mode which does it only when it appears to actually help. +;; mode which does it only when it appears to actually help. Also use a minimum +;; benefit of 0 to make it easy to write small testcases in careful mode. -;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS -;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL +;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -S -o - | filecheck %s --check-prefix CAREFUL (module ;; ALWAYS: (type $A (sub (struct))) |