diff options
author | Alon Zakai <azakai@google.com> | 2021-09-16 20:03:37 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-17 03:03:37 +0000 |
commit | eb83c4257976f9536b98df43e8f2242a102b3ce4 (patch) | |
tree | b0ee4c4b23744c52723eb66de414fc3e18ac695c /test/lit/passes/inlining_splitting.wast | |
parent | 8e8bce1b59a8d60a35e21798c6aec95e3dcade6b (diff) | |
download | binaryen-eb83c4257976f9536b98df43e8f2242a102b3ce4.tar.gz binaryen-eb83c4257976f9536b98df43e8f2242a102b3ce4.tar.bz2 binaryen-eb83c4257976f9536b98df43e8f2242a102b3ce4.zip |
Partial inlining via function splitting (#4152)
This PR helps with functions like this:
function foo(x) {
if (x) {
..
lots of work here
..
}
}
If "lots of work" is large enough, then we won't inline such a
function. However, we may end up calling into the function
only to get a false on that if and immediately exit. So it is useful
to partially inline this function, basically by creating a split
of it into a condition part that is inlineable
function foo$inlineable(x) {
if (x) {
foo$outlined();
}
}
and an outlined part that is not inlineable:
function foo$outlined(x) {
..
lots of work here
..
}
We can then inline the inlineable part. That means that a call
like
foo(param);
turns into
if (param) {
foo$outlined();
}
In other words, we end up replacing a call and then a check with
a check and then a call. Any time that the condition is false, this
will be a speedup.
The cost here is increased size, as we duplicate the condition
into the callsites. For that reason, only do this when heavily
optimizing for size.
This is a 10% speedup on j2cl. This helps two types of functions
there: Java class inits, which often look like "have I been
initialized before? if not, do all this work", and also assertion
methods which look like "if the input is null, throw an
exception".
Diffstat (limited to 'test/lit/passes/inlining_splitting.wast')
-rw-r--r-- | test/lit/passes/inlining_splitting.wast | 1702 |
1 files changed, 1702 insertions, 0 deletions
diff --git a/test/lit/passes/inlining_splitting.wast b/test/lit/passes/inlining_splitting.wast new file mode 100644 index 000000000..98168527a --- /dev/null +++ b/test/lit/passes/inlining_splitting.wast @@ -0,0 +1,1702 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --all-features -S -o - | filecheck %s + +(module + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $i32_=>_none (func (param i32))) + + ;; CHECK: (type $anyref_=>_anyref (func (param anyref) (result anyref))) + + ;; CHECK: (type $anyref_=>_none (func (param anyref))) + + ;; CHECK: (type $struct (struct )) + (type $struct (struct)) + + ;; CHECK: (type $i32_rtt_$struct_=>_none (func (param i32 (rtt $struct)))) + + ;; CHECK: (type $i64_i32_f64_=>_none (func (param i64 i32 f64))) + + ;; CHECK: (import "out" "func" (func $import)) + (import "out" "func" (func $import)) + + ;; CHECK: (global $glob i32 (i32.const 1)) + (global $glob i32 (i32.const 1)) + + ;; CHECK: (start $start-used-globally) + (start $start-used-globally) + + ;; Pattern A: functions beginning with + ;; + ;; if (simple) return; + + (func $maybe-work-hard (param $x i32) + ;; A function that does a quick check before any heavy work. We can outline + ;; the heavy work, so that the condition can be inlined. + ;; + ;; This function (and others lower down that we also optimize) will vanish + ;; in the output. Part of it will be inlined into its caller, below, and + ;; the rest will be outlined into a new function with suffix "outlined". + (if + (local.get $x) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-maybe-work-hard + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-work-hard + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$maybe-work-hard + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-work-hard0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$maybe-work-hard + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-work-hard1 + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$maybe-work-hard + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-maybe-work-hard + ;; Call the above function to verify that we can in fact inline it after + ;; splitting. We should see each of these three calls replaced by inlined + ;; code performing the if from $maybe-work-hard, and depending on that + ;; result they each call the outlined code that must *not* be inlined. + ;; + ;; Note that we must call more than once, otherwise given a single use we + ;; will always inline the entire thing. + (call $maybe-work-hard (i32.const 1)) + (call $maybe-work-hard (i32.const 2)) + (call $maybe-work-hard (i32.const 3)) + ) + + (func $just-if (param $x i32) + ;; As above, but all we have is an if. + (if + (local.get $x) + (loop $l + (call $import) + (br_if $l + (local.get $x) + ) + ) + ) + ) + + ;; CHECK: (func $call-just-if + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$just-if + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (call $byn-split-outlined-B$just-if + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$just-if0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (call $byn-split-outlined-B$just-if + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-just-if + (call $just-if (i32.const 1)) + (call $just-if (i32.const 2)) + ) + + ;; CHECK: (func $br-to-toplevel (param $x i32) + ;; CHECK-NEXT: (block $toplevel + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (br $toplevel) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-to-toplevel (param $x i32) + (block $toplevel + (if + (local.get $x) + (block + (if + (local.get $x) + ;; A br to the toplevel block prevents us from outlining this code, + ;; as we can't outline a br without its target. + (br $toplevel) + (call $import) + ) + ) + ) + ) + ) + + ;; CHECK: (func $call-br-to-toplevel + ;; CHECK-NEXT: (call $br-to-toplevel + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $br-to-toplevel + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-br-to-toplevel + (call $br-to-toplevel (i32.const 1)) + (call $br-to-toplevel (i32.const 2)) + ) + + ;; CHECK: (func $nondefaultable-param (param $x i32) (param $y (rtt $struct)) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nondefaultable-param (param $x i32) (param $y (rtt $struct)) + ;; The RTT param here prevents us from even being inlined, even with + ;; splitting. + (if + (local.get $x) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-nondefaultable-param + ;; CHECK-NEXT: (call $nondefaultable-param + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (rtt.canon $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-nondefaultable-param + (call $nondefaultable-param (i32.const 0) (rtt.canon $struct)) + ) + + (func $many-params (param $x i64) (param $y i32) (param $z f64) + ;; Test that we can optimize this function even though it has multiple + ;; parameters, and it is not the very first one that we use in the + ;; condition. + (if + (local.get $y) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-many-params + ;; CHECK-NEXT: (local $0 i64) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 f64) + ;; CHECK-NEXT: (local $3 i64) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local $5 f64) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$many-params + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (f64.const 3.14159) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$many-params + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$many-params0 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $5 + ;; CHECK-NEXT: (f64.const 3.14159) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$many-params + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-many-params + ;; Call the above function to verify that we can in fact inline it after + ;; splitting. We should see each of these three calls replaced by inlined + ;; code performing the if from $maybe-work-hard, and depending on that + ;; result they each call the outlined code that must *not* be inlined. + (call $many-params (i64.const 0) (i32.const 1) (f64.const 3.14159)) + (call $many-params (i64.const 0) (i32.const 1) (f64.const 3.14159)) + ) + + (func $condition-eqz (param $x i32) + (if + ;; More work in the condition, but work that we still consider worth + ;; optimizing: a unary op. + (i32.eqz + (local.get $x) + ) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-condition-eqz + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-eqz + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-eqz + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-eqz0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-condition-eqz + (call $condition-eqz (i32.const 0)) + (call $condition-eqz (i32.const 1)) + ) + + (func $condition-global + (if + ;; A global read, also worth splitting. + (global.get $glob) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-condition-global + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-global + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-global) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-global0 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-global) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-condition-global + (call $condition-global) + (call $condition-global) + ) + + (func $condition-ref.is (param $x anyref) + (if + ;; A ref.is operation. + (ref.is_null + (local.get $x) + ) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-condition-ref.is + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-ref.is + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-ref.is + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$condition-ref.is0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$condition-ref.is + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-condition-ref.is + (call $condition-ref.is (ref.null any)) + (call $condition-ref.is (ref.null any)) + ) + + ;; CHECK: (func $condition-disallow-binary (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $condition-disallow-binary (param $x i32) + (if + ;; Work we do *not* allow (at least for now), a binary. + (i32.add + (local.get $x) + (local.get $x) + ) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-condition-disallow-binary + ;; CHECK-NEXT: (call $condition-disallow-binary + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $condition-disallow-binary + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-condition-disallow-binary + (call $condition-disallow-binary (i32.const 0)) + (call $condition-disallow-binary (i32.const 1)) + ) + + ;; CHECK: (func $condition-disallow-unreachable (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $condition-disallow-unreachable (param $x i32) + (if + ;; Work we do *not* allow (at least for now), an unreachable. + (i32.eqz + (unreachable) + ) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-condition-disallow-unreachable + ;; CHECK-NEXT: (call $condition-disallow-unreachable + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $condition-disallow-unreachable + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-condition-disallow-unreachable + (call $condition-disallow-unreachable (i32.const 0)) + (call $condition-disallow-unreachable (i32.const 1)) + ) + + ;; CHECK: (func $start-used-globally + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $start-used-globally + ;; This is optimizable even though it is the start function, that is is, + ;; having global uses of a function are not a problem for partial inlining + ;; (since we do not modify this function - we create new split pieces of + ;; it). + (if + (global.get $glob) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-start-used-globally + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$start-used-globally + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$start-used-globally) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$start-used-globally0 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$start-used-globally) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-start-used-globally + (call $start-used-globally) + (call $start-used-globally) + ) + + (func $inlineable + ;; This looks optimizable, but it is also inlineable - so we do not need to + ;; split it. It will just be inlined directly, without any $outlined part + ;; that is split out. + (if + (global.get $glob) + (return) + ) + ) + + ;; CHECK: (func $call-inlineable + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$inlineable + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: (br $__inlined_func$inlineable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$inlineable0 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $glob) + ;; CHECK-NEXT: (br $__inlined_func$inlineable0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-inlineable + (call $inlineable) + (call $inlineable) + ) + + ;; CHECK: (func $if-not-first (param $x i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-not-first (param $x i32) + ;; Except for the initial nop, we should outline this. As the if is not + ;; first any more, we ignore it. + (nop) + (if + (local.get $x) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-if-not-first + ;; CHECK-NEXT: (call $if-not-first + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-not-first + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-if-not-first + (call $if-not-first (i32.const 0)) + (call $if-not-first (i32.const 1)) + ) + + ;; CHECK: (func $if-else (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-else (param $x i32) + ;; An else in the if prevents us from recognizing the pattern we want. + (if + (local.get $x) + (return) + (nop) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-if-else + ;; CHECK-NEXT: (call $if-else + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-else + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-if-else + (call $if-else (i32.const 0)) + (call $if-else (i32.const 1)) + ) + + ;; CHECK: (func $if-non-return (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $l + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (br $l) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-non-return (param $x i32) + ;; Something other than a return in the if body prevents us from outlining. + (if + (local.get $x) + (unreachable) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-if-non-return + ;; CHECK-NEXT: (call $if-non-return + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $if-non-return + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-if-non-return + (call $if-non-return (i32.const 0)) + (call $if-non-return (i32.const 1)) + ) + + (func $colliding-name (param $x i32) + ;; When we outline this, the name should not collide with that of the + ;; function after us. + (if + (local.get $x) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; CHECK: (func $call-colliding-name + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$colliding-name + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_0 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$colliding-name0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-colliding-name + (call $colliding-name (i32.const 0)) + (call $colliding-name (i32.const 1)) + ) + + ;; CHECK: (func $byn-split-outlined-A$colliding-name + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $byn-split-outlined-A$colliding-name + ;; This function's name might collide with the split function we create for + ;; the above function; the split function's name must be fixed up. + ) + + ;; Pattern B: functions containing + ;; + ;; if (simple1) heavy-work-that-is-unreachable; + ;; if (simple..) heavy-work-that-is-unreachable; + ;; simplek + + (func $error-if-null (param $x anyref) (result anyref) + ;; A "as non null" function: If the input is null, issue an error somehow + ;; (here, by calling an import, but could also be a throwing of an + ;; exception). If not null, return the value. + (if + (ref.is_null + (local.get $x) + ) + (block + (call $import) + (unreachable) + ) + ) + (local.get $x) + ) + + ;; CHECK: (func $call-error-if-null + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$error-if-null (result anyref) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__inlined_func$byn-split-inlineable-B$error-if-null + ;; CHECK-NEXT: (call $byn-split-outlined-B$error-if-null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$error-if-null0 (result anyref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__inlined_func$byn-split-inlineable-B$error-if-null0 + ;; CHECK-NEXT: (call $byn-split-outlined-B$error-if-null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-error-if-null + (drop (call $error-if-null (ref.null any))) + (drop (call $error-if-null (ref.null any))) + ) + + ;; CHECK: (func $too-many (param $x anyref) (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + (func $too-many (param $x anyref) (result anyref) + (if + (ref.is_null + (local.get $x) + ) + (block + (call $import) + (unreachable) + ) + ) + (nop) ;; An extra operation here prevents us from identifying the pattern. + (local.get $x) + ) + + ;; CHECK: (func $call-too-many + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $too-many + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $too-many + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-too-many + (drop (call $too-many (ref.null any))) + (drop (call $too-many (ref.null any))) + ) + + ;; CHECK: (func $tail-not-simple (param $x anyref) (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $tail-not-simple (param $x anyref) (result anyref) + (if + (ref.is_null + (local.get $x) + ) + (block + (call $import) + (unreachable) + ) + ) + (unreachable) ;; This prevents us from optimizing + ) + + ;; CHECK: (func $call-tail-not-simple + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-not-simple + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-not-simple + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-tail-not-simple + (drop (call $tail-not-simple (ref.null any))) + (drop (call $tail-not-simple (ref.null any))) + ) + + (func $reachable-if-body (param $x anyref) (result anyref) + (if + (ref.is_null + (local.get $x) + ) + ;; It is ok if the body is not unreachable (so long as it contains no + ;; returns). We will optimize this, and just do a call to the outlined + ;; code, without a return of a value here. + (call $import) + ) + (local.get $x) + ) + + ;; CHECK: (func $call-reachable-if-body + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $3 anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$reachable-if-body (result anyref) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-outlined-B$reachable-if-body + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$reachable-if-body0 (result anyref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-outlined-B$reachable-if-body0 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-reachable-if-body + ;; Note that the above contains + ;; + ;; (block $__inlined_func$byn-split-outlined-B$reachable-if-body + ;; + ;; which indicates that we've inlined the outlined function. That seems odd, + ;; but it is the result of the if's body being just a call. When we outline, + ;; we end up with a function that all it does is make that call - which is + ;; worth inlining in the normal way (to avoid two calls). As a result of all + ;; that, we end up inlining *all* of $reachable-if-body, just by a + ;; roundabout way (split, outline, then inline). While this seems odd, each + ;; step along the way makes sense, and the result is a good one (might be a + ;; little hard to see before opts remove the extra block cruft etc.). + ;; + ;; We could avoid this if we detected that the if body is just a call, and + ;; not done any outlining - just done that call. That would be more + ;; efficient, but it would make the code more complicated, and the result is + ;; the same. + (drop (call $reachable-if-body (ref.null any))) + (drop (call $reachable-if-body (ref.null any))) + ) + + ;; CHECK: (func $reachable-if-body-return (param $x anyref) (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + (func $reachable-if-body-return (param $x anyref) (result anyref) + (if + (ref.is_null + (local.get $x) + ) + (if + (i32.const 1) + ;; The return here prevents the optimization. + (return + (local.get $x) + ) + (call $import) + ) + ) + (local.get $x) + ) + + ;; CHECK: (func $call-reachable-if-body-return + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $reachable-if-body-return + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $reachable-if-body-return + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-reachable-if-body-return + (drop (call $reachable-if-body-return (ref.null any))) + (drop (call $reachable-if-body-return (ref.null any))) + ) + + (func $unreachable-if-body-no-result (param $x anyref) + (if + (ref.is_null + (local.get $x) + ) + ;; The if body is unreachable, but the function has no returned value. + ;; When we outline this code, we should not try to return a value. + (block + (call $import) + (unreachable) + ) + ) + ) + + ;; CHECK: (func $call-unreachable-if-body-no-result + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$unreachable-if-body-no-result + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-B$unreachable-if-body-no-result + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$unreachable-if-body-no-result0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-B$unreachable-if-body-no-result + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-unreachable-if-body-no-result + (call $unreachable-if-body-no-result (ref.null any)) + (call $unreachable-if-body-no-result (ref.null any)) + ) + + (func $multi-if (param $x anyref) (result anyref) + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + ;; A second if. We can outline both if bodies. + (if + (ref.is_func + (local.get $x) + ) + (loop $x + (call $import) + (br_if $x + (global.get $glob) + ) + ) + ) + (local.get $x) + ) + + ;; CHECK: (func $call-multi-if + ;; CHECK-NEXT: (local $0 anyref) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $3 anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$multi-if (result anyref) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-outlined-B$multi-if + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_0 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$multi-if0 (result anyref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-outlined-B$multi-if0 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_func + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-multi-if + (drop (call $multi-if (ref.null any))) + (drop (call $multi-if (ref.null func))) + ) + + ;; CHECK: (func $too-many-ifs (param $x anyref) (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + (func $too-many-ifs (param $x anyref) (result anyref) + ;; 5 ifs, which is too many. + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + (if + (ref.is_null + (local.get $x) + ) + (call $import) + ) + (local.get $x) + ) + + ;; CHECK: (func $call-too-many-ifs + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $too-many-ifs + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $too-many-ifs + ;; CHECK-NEXT: (ref.null func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-too-many-ifs + (drop (call $too-many-ifs (ref.null any))) + (drop (call $too-many-ifs (ref.null func))) + ) +) + +;; CHECK: (func $byn-split-outlined-A$maybe-work-hard (param $x i32) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-B$just-if (param $x i32) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br_if $l +;; CHECK-NEXT: (local.get $x) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$many-params (param $x i64) (param $y i32) (param $z f64) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$condition-eqz (param $x i32) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$condition-global +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$condition-ref.is (param $x anyref) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$start-used-globally +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$colliding-name_0 (param $x i32) +;; CHECK-NEXT: (loop $l +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br $l) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-B$error-if-null (param $x anyref) (result anyref) +;; CHECK-NEXT: (block $block +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-B$unreachable-if-body-no-result (param $x anyref) +;; CHECK-NEXT: (block $block +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-B$multi-if_0 (param $x anyref) +;; CHECK-NEXT: (loop $x +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: (br_if $x +;; CHECK-NEXT: (global.get $glob) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; CHECK: (type $none_=>_none (func)) + (type $none_=>_none (func)) + ;; CHECK: (global $global$0 (mut i32) (i32.const 10)) + (global $global$0 (mut i32) (i32.const 10)) + ;; CHECK: (export "0" (func $0)) + (export "0" (func $0)) + ;; CHECK: (export "1" (func $1)) + (export "1" (func $1)) + ;; CHECK: (func $0 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$1 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$10 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $0 + ;; A function that is a good candidate to partially inline. + (if + (global.get $global$0) + (return) + ) + (call $1) + (call $1) + ) + ;; CHECK: (func $1 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$0 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$byn-split-outlined-A$0 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$1 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$00 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$10 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$01 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $byn-split-outlined-A$0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $1 + ;; This and the previous function are mutually recursive. As a result, we + ;; can keep partially inlining between them, creating new functions as we + ;; go, even though that is not very useful. + (call $0) + ) + + ;; Add a lot more functions, so the # of functions is high which would + ;; otherwise limit the # of iterations that we run. + ;; CHECK: (func $2 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $2 + (nop) + ) + ;; CHECK: (func $3 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $3 + (nop) + ) + ;; CHECK: (func $4 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $4 + (nop) + ) + ;; CHECK: (func $5 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $5 + (nop) + ) + ;; CHECK: (func $6 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $6 + (nop) + ) + ;; CHECK: (func $7 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $7 + (nop) + ) + ;; CHECK: (func $8 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $8 + (nop) + ) + ;; CHECK: (func $9 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $9 + (nop) + ) + ;; CHECK: (func $10 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $10 + (nop) + ) + ;; CHECK: (func $11 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $11 + (nop) + ) + ;; CHECK: (func $12 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $12 + (nop) + ) + ;; CHECK: (func $13 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $13 + (nop) + ) + ;; CHECK: (func $14 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $14 + (nop) + ) + ;; CHECK: (func $15 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $15 + (nop) + ) + ;; CHECK: (func $16 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $16 + (nop) + ) + ;; CHECK: (func $17 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $17 + (nop) + ) + ;; CHECK: (func $18 + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $18 + (nop) + ) +) +;; CHECK: (func $byn-split-outlined-A$0 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$1 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$0 +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eqz +;; CHECK-NEXT: (global.get $global$0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $byn-split-outlined-A$0_0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$10 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$00 +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eqz +;; CHECK-NEXT: (global.get $global$0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $byn-split-outlined-A$0_0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$0_0 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$1 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$0 +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eqz +;; CHECK-NEXT: (global.get $global$0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $byn-split-outlined-A$0_1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$10 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$00 +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eqz +;; CHECK-NEXT: (global.get $global$0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (call $byn-split-outlined-A$0_1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $byn-split-outlined-A$0_1 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$1 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (block +;; CHECK-NEXT: (block $__inlined_func$10 +;; CHECK-NEXT: (block +;; CHECK-NEXT: (call $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) |