diff options
author | Alon Zakai <azakai@google.com> | 2023-12-05 12:41:21 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-05 12:41:21 -0800 |
commit | 66277f9b767cb1b45ce12b77109c2538b6cb9c12 (patch) | |
tree | 31dc89d2894fdd8805ba6b1e8db11aa3b6703555 /test | |
parent | 42cddbf88ebe74d17490e2dd13826e6da9104b15 (diff) | |
download | binaryen-66277f9b767cb1b45ce12b77109c2538b6cb9c12.tar.gz binaryen-66277f9b767cb1b45ce12b77109c2538b6cb9c12.tar.bz2 binaryen-66277f9b767cb1b45ce12b77109c2538b6cb9c12.zip |
Inlining: Inline trivial calls (#6143)
A trivial call is something like a function that just calls another immediately,
function foo(x, y) {
return bar(y, 15);
}
We can inline those and expect to benefit in most cases, though we might
increase code size slightly. Hence it makes sense to inline such cases, even
though in general we are careful and do not inline functions with calls in
them; a "trampoline" like that likely has most of the work in the call itself,
which we can avoid by inlining.
Suggested based on findings in Java.
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/inlining-optimizing_optimize-level=3.wast | 19 | ||||
-rw-r--r-- | test/lit/passes/inlining_optimize-level=3.wast | 155 |
2 files changed, 161 insertions, 13 deletions
diff --git a/test/lit/passes/inlining-optimizing_optimize-level=3.wast b/test/lit/passes/inlining-optimizing_optimize-level=3.wast index fd646d5b4..a44429688 100644 --- a/test/lit/passes/inlining-optimizing_optimize-level=3.wast +++ b/test/lit/passes/inlining-optimizing_optimize-level=3.wast @@ -16,10 +16,10 @@ ;; CHECK: (type $FUNCSIG$i (func (result i32))) - ;; CHECK: (type $5 (func (param i32 i32 i32 i32) (result i32))) - ;; CHECK: (type $FUNCSIG$vii (func (param i32 i32))) + ;; CHECK: (type $6 (func (param i32 i32 i32 i32) (result i32))) + ;; CHECK: (type $FUNCSIG$v (func)) (type $FUNCSIG$v (func)) (type $FUNCSIG$i (func (result i32))) @@ -6487,11 +6487,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $12 - ;; CHECK-NEXT: (call $___udivdi3 + ;; CHECK-NEXT: (call $___udivmoddi4 ;; CHECK-NEXT: (local.get $12) ;; CHECK-NEXT: (local.get $20) ;; CHECK-NEXT: (i32.const 1000000000) ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br_if $while-in66 @@ -14813,11 +14814,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (call $___udivdi3 + ;; CHECK-NEXT: (call $___udivmoddi4 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: (i32.const 10) ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if @@ -30696,15 +30698,6 @@ ) (local.get $3) ) - ;; CHECK: (func $___udivdi3 (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - ;; CHECK-NEXT: (call $___udivmoddi4 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $___udivdi3 (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) (call $___udivmoddi4 (local.get $0) diff --git a/test/lit/passes/inlining_optimize-level=3.wast b/test/lit/passes/inlining_optimize-level=3.wast index b993137c1..c03f7f117 100644 --- a/test/lit/passes/inlining_optimize-level=3.wast +++ b/test/lit/passes/inlining_optimize-level=3.wast @@ -563,3 +563,158 @@ (unreachable) ) ) + +;; Inlining of trivial calls in the middle. +(module + (table 10 funcref) + + ;; Refer to the middle functions so that we do not inline them as single-use + ;; functions (which would be a trivial case, not related to trivial calls). + (elem (i32.const 0) $middle1 $middle2 $middle3) + + ;; CHECK: (type $0 (func (param i32 i32 i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (table $0 10 funcref) + + ;; CHECK: (elem $0 (i32.const 0) $middle1 $middle2 $middle3) + + ;; CHECK: (func $top (param $x i32) (param $y i32) (param $z i32) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (br $loop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $top (param $x i32) (param $y i32) (param $z i32) + ;; This top function will not be inlined. + (loop $loop + (br $loop) + ) + ;; Add to the size so it isn't inlined as a tiny function. + (nop) + (nop) + ) + + ;; CHECK: (func $middle1 (param $x i32) (param $y i32) (param $z i32) + ;; CHECK-NEXT: (call $top + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $middle1 (param $x i32) (param $y i32) (param $z i32) + ;; This function is a trivial call, which we can inline to the bottom. + (call $top + (local.get $x) + (local.get $y) + (local.get $z) + ) + ) + + ;; CHECK: (func $middle2 (param $x i32) (param $y i32) (param $z i32) + ;; CHECK-NEXT: (call $top + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $middle2 (param $x i32) (param $y i32) (param $z i32) + ;; Also trivial, even though the order of params is different and we have a + ;; const. + (call $top + (local.get $z) + (i32.const 42) + (local.get $x) + ) + ) + + ;; CHECK: (func $middle3 (param $x i32) (param $y i32) (param $z i32) + ;; CHECK-NEXT: (call $top + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $middle3 (param $x i32) (param $y i32) (param $z i32) + ;; Not trivial, becaues of the eqz. + (call $top + (local.get $z) + (i32.eqz + (i32.const 42) + ) + (local.get $x) + ) + ) + + ;; CHECK: (func $bottom + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local $5 i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$middle1 + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $top + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$middle2$1 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $5 + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $top + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $middle3 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $bottom + ;; The first two will be inlined. + (call $middle1 + (i32.const 1) + (i32.const 2) + (i32.const 3) + ) + (call $middle2 + (i32.const 1) + (i32.const 2) + (i32.const 3) + ) + (call $middle3 + (i32.const 1) + (i32.const 2) + (i32.const 3) + ) + ) +) |