| ;; 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 --partial-inlining-ifs=4 --all-features -S -o - | filecheck %s |
| |
| (module |
| ;; CHECK: (type $func.0 (func)) |
| |
| ;; CHECK: (type $func.1 (func (param i32))) |
| |
| ;; CHECK: (type $func.2 (func (param anyref) (result anyref))) |
| |
| ;; CHECK: (type $func.3 (func (param anyref))) |
| |
| ;; CHECK: (type $struct (struct)) |
| (type $struct (struct)) |
| |
| ;; CHECK: (type $func.4 (func (param i32 (rtt $struct)))) |
| |
| ;; CHECK: (type $func.5 (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: ) |