summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-08-14 10:45:01 -0700
committerGitHub <noreply@github.com>2024-08-14 10:45:01 -0700
commit39553a0247eb39c2f58c883cd6fb6b7e00f559ad (patch)
tree13a422451627b4011e2963c1b1e6304fa65421f5
parent0c84afe87ea67239c4d7bf885b43b5c4f52322af (diff)
downloadbinaryen-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-xscripts/fuzz_opt.py5
-rw-r--r--src/passes/Monomorphize.cpp59
-rw-r--r--test/lit/passes/monomorphize-benefit.wast1652
-rw-r--r--test/lit/passes/monomorphize-consts.wast7
-rw-r--r--test/lit/passes/monomorphize-context.wast7
-rw-r--r--test/lit/passes/monomorphize-drop.wast7
-rw-r--r--test/lit/passes/monomorphize-types.wast7
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)))