summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoktug Gokdogan <goktug@google.com>2023-05-04 09:48:29 -0700
committerGitHub <noreply@github.com>2023-05-04 09:48:29 -0700
commite9b8e53812610e43260f6db4a05fd12d96a3f449 (patch)
treeb995c0b6eaa2b318aa380f3448089cf37aee2659
parent070542f9f30cc262ac741fb3a620037900ac6381 (diff)
downloadbinaryen-e9b8e53812610e43260f6db4a05fd12d96a3f449.tar.gz
binaryen-e9b8e53812610e43260f6db4a05fd12d96a3f449.tar.bz2
binaryen-e9b8e53812610e43260f6db4a05fd12d96a3f449.zip
Fallback to direct inlining if the outline will be inlined. (#5698)
This workarounds the extra work around the edge case where; - Function is too big to full-inline - It is a candidate for partial inline - Outlined version becomes eligible for full-inline. In such a case, binaryen would introduce a temporary state with partial inlined functions and later on inline them. J2CL hit this scenario for String literal which resulted in significant regressions in compilation time. This patch updates partial inlining analysis to identify the edge case and direct to full-inlining when that happens.
-rw-r--r--src/passes/Inlining.cpp42
-rw-r--r--test/lit/passes/inlining_splitting.wast571
2 files changed, 573 insertions, 40 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 511271f42..112c31036 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -552,7 +552,12 @@ struct FunctionSplitter {
// into account (like limitations on which functions can be inlined into in
// each iteration, the number of iterations, etc.). Therefore this function
// may only find out if we *can* split, but not actually do any splitting.
- InliningMode getSplitInliningMode(Function* func) {
+ //
+ // Note that to avoid wasteful work, this function may return "Full' inlining
+ // mode instead of a split inining. That is; if it detects that a partial
+ // inlining will trigger a follow up full inline of the splitted function
+ // it will instead return "InliningMode::Full" directly.
+ InliningMode getSplitDrivenInliningMode(Function* func, FunctionInfo& info) {
auto* body = func->body;
// If the body is a block, and we have breaks to that block, then we cannot
@@ -588,6 +593,13 @@ struct FunctionSplitter {
// return), and we would not even attempt to do splitting.
assert(body->is<Block>());
+ auto outlinedFunctionSize = info.size - Measurer::measure(iff);
+ // If outlined function will be worth normal inline, skip the intermediate
+ // state and inline fully now.
+ if (outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
+ return InliningMode::Full;
+ }
+
return InliningMode::SplitPatternA;
}
@@ -666,8 +678,17 @@ struct FunctionSplitter {
assert(iff->ifTrue->type == Type::unreachable);
}
}
-
// Success, this matches the pattern.
+
+ // If the outlined function will be worth inlining normally, skip the
+ // intermediate state and inline fully now.
+ if (numIfs == 1) {
+ auto outlinedFunctionSize = Measurer::measure(iff->ifTrue);
+ if (outlinedFunctionWorthInlining(info, outlinedFunctionSize)) {
+ return InliningMode::Full;
+ }
+ }
+
return InliningMode::SplitPatternB;
}
@@ -730,6 +751,19 @@ private:
// staying constant.
std::unordered_map<Name, Split> splits;
+ bool outlinedFunctionWorthInlining(FunctionInfo& origin, Index sizeEstimate) {
+ FunctionInfo info;
+ // Start with a copy of the origin's info, and apply the size estimate.
+ // This is not accurate, for example the origin function may have
+ // loop or calls even though this section may not have.
+ // This is a conservative estimate, that is, it will return true only when
+ // it should, but might return false when a more precise analysis would
+ // return true. And it is a practical estimation to avoid extra future work.
+ info = origin;
+ info.size = sizeEstimate;
+ return info.worthFullInlining(options);
+ }
+
Function* doSplit(Function* func, InliningMode inliningMode) {
Builder builder(*module);
@@ -1084,8 +1118,8 @@ struct Inlining : public Pass {
// Otherwise, check if we can at least inline part of it, if we are
// interested in such things.
if (functionSplitter) {
- info.inliningMode =
- functionSplitter->getSplitInliningMode(module->getFunction(name));
+ info.inliningMode = functionSplitter->getSplitDrivenInliningMode(
+ module->getFunction(name), info);
return info.inliningMode;
}
diff --git a/test/lit/passes/inlining_splitting.wast b/test/lit/passes/inlining_splitting.wast
index 74039533f..d1bc934dc 100644
--- a/test/lit/passes/inlining_splitting.wast
+++ b/test/lit/passes/inlining_splitting.wast
@@ -768,7 +768,7 @@
;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_65
+ ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_67
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -783,7 +783,7 @@
;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_65
+ ;; CHECK-NEXT: (call $byn-split-outlined-A$colliding-name_67
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -972,7 +972,13 @@
;; 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)
+ (block
+ ;; We need to have a loop here to avoid normal inlining from kicking in
+ ;; on the outlined code.
+ (loop $loop
+ (call $import)
+ )
+ )
)
(local.get $x)
)
@@ -980,8 +986,6 @@
;; CHECK: (func $call-reachable-if-body (type $none_=>_none)
;; 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)
@@ -993,11 +997,8 @@
;; 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: (call $byn-split-outlined-B$reachable-if-body
+ ;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
@@ -1016,11 +1017,8 @@
;; 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: (call $byn-split-outlined-B$reachable-if-body
+ ;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $1)
@@ -1030,27 +1028,69 @@
;; 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)))
)
+ (func $reachable-if-body-noloop (param $x anyref) (result anyref)
+ ;; As above, but without a loop.
+ (if
+ (ref.is_null
+ (local.get $x)
+ )
+ (call $import)
+ )
+ (local.get $x)
+ )
+
+ ;; CHECK: (func $call-reachable-if-body-noloop (type $none_=>_none)
+ ;; CHECK-NEXT: (local $0 anyref)
+ ;; CHECK-NEXT: (local $1 anyref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result anyref)
+ ;; CHECK-NEXT: (block $__inlined_func$reachable-if-body-noloop (result anyref)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result anyref)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (ref.is_null
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $import)
+ ;; 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$reachable-if-body-noloop0 (result anyref)
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result anyref)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (ref.is_null
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $import)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-reachable-if-body-noloop
+ ;; As above, but the called function has no loop. In that case, even though
+ ;; it fits the pattern for partial inlining we can just inline the entire
+ ;; thing normally.
+ (drop (call $reachable-if-body-noloop (ref.null any)))
+ (drop (call $reachable-if-body-noloop (ref.null any)))
+ )
+
;; CHECK: (func $reachable-if-body-return (type $anyref_=>_anyref) (param $x anyref) (result anyref)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (ref.is_null
@@ -1202,7 +1242,7 @@
;; CHECK-NEXT: (ref.is_null
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_74
+ ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_76
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -1233,7 +1273,7 @@
;; CHECK-NEXT: (ref.is_null
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_74
+ ;; CHECK-NEXT: (call $byn-split-outlined-B$multi-if_76
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -1385,7 +1425,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
-;; CHECK: (func $byn-split-outlined-A$colliding-name_65 (type $i32_=>_none) (param $x i32)
+;; CHECK: (func $byn-split-outlined-A$colliding-name_67 (type $i32_=>_none) (param $x i32)
;; CHECK-NEXT: (loop $l
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: (br $l)
@@ -1397,12 +1437,18 @@
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
+;; CHECK: (func $byn-split-outlined-B$reachable-if-body (type $anyref_=>_none) (param $x anyref)
+;; CHECK-NEXT: (loop $loop
+;; CHECK-NEXT: (call $import)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
;; CHECK: (func $byn-split-outlined-B$unreachable-if-body-no-result (type $anyref_=>_none) (param $x anyref)
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
-;; CHECK: (func $byn-split-outlined-B$multi-if_74 (type $anyref_=>_none) (param $x anyref)
+;; CHECK: (func $byn-split-outlined-B$multi-if_76 (type $anyref_=>_none) (param $x anyref)
;; CHECK-NEXT: (loop $x
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: (br_if $x
@@ -1611,6 +1657,7 @@
(nop)
)
)
+
;; CHECK: (func $byn-split-outlined-A$0 (type $none_=>_none)
;; CHECK-NEXT: (block
;; CHECK-NEXT: (block $__inlined_func$1
@@ -1697,3 +1744,455 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
+(module
+ (func $middle-size-A (param $x i32)
+ ;; This function is too big for normal inlining with the default size limit.
+ ;; However, if we partially inline it then the outlined code becomes small
+ ;; enough to be normally inlined. We should just inline it normally in that
+ ;; case, to avoid wasted work.
+ (if
+ (local.get $x)
+ (return)
+ )
+ ;; 6x3 = 18 items, close to the default size limit of 20. With the if, we
+ ;; hit that limit and are too big. But if we did partial inlining then the
+ ;; lines below us are small enough to then be inlined normally.
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $i32_=>_none (func (param i32)))
+
+ ;; CHECK: (type $i32_=>_i32 (func (param i32) (result i32)))
+
+ ;; CHECK: (func $call-$middle-size-A (type $none_=>_none)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block $__inlined_func$middle-size-A
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (br $__inlined_func$middle-size-A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block $__inlined_func$middle-size-A0
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: (br $__inlined_func$middle-size-A0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-$middle-size-A
+ ;; This will be normally inlined, see the comment in the above function.
+ ;; We can see it is normally inlined and not partially from the string
+ ;; "__inlined_func" in the code (instead of "split" appearing anywhere).
+ (call $middle-size-A
+ (i32.const 0)
+ )
+ (call $middle-size-A
+ (i32.const 1)
+ )
+ )
+
+ (func $big-size-A (param $x i32)
+ ;; As above, but a little larger - so large that we won't normally inline
+ ;; it.
+ (if
+ (local.get $x)
+ (return)
+ )
+ ;; 6x4 = 24 items, which is more than the inlining limit.
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ )
+
+ ;; CHECK: (func $call-$big-size-A (type $none_=>_none)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-A$big-size-A
+ ;; 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$big-size-A
+ ;; 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$big-size-A0
+ ;; 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$big-size-A
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-$big-size-A
+ ;; Normal inlining can't work here, so we'll just do partial inlining.
+ (call $big-size-A
+ (i32.const 0)
+ )
+ (call $big-size-A
+ (i32.const 1)
+ )
+ )
+
+ (func $middle-size-B (param $x i32) (result i32)
+ ;; As above, but for pattern B and not A.
+ (if
+ (local.get $x)
+ (block
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (unreachable)
+ )
+ )
+ (local.get $x)
+ )
+
+ ;; CHECK: (func $call-$middle-size-B (type $none_=>_none)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (block $__inlined_func$middle-size-B (result i32)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (block $__inlined_func$middle-size-B0 (result i32)
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-$middle-size-B
+ ;; We will normally inline here.
+ (drop
+ (call $middle-size-B
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $middle-size-B
+ (i32.const 1)
+ )
+ )
+ )
+
+ (func $big-size-B (param $x i32) (result i32)
+ ;; As above, but a little larger - so large that we won't normally inline
+ ;; it.
+ (if
+ (local.get $x)
+ (block
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (drop (i32.const 0)) (drop (i32.const 0)) (drop (i32.const 0))
+ (unreachable)
+ )
+ )
+ (local.get $x)
+ )
+
+ ;; CHECK: (func $call-$big-size-B (type $none_=>_none)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$big-size-B (result i32)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (br $__inlined_func$byn-split-inlineable-B$big-size-B
+ ;; CHECK-NEXT: (call $byn-split-outlined-B$big-size-B
+ ;; 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 i32)
+ ;; CHECK-NEXT: (block $__inlined_func$byn-split-inlineable-B$big-size-B0 (result i32)
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: (br $__inlined_func$byn-split-inlineable-B$big-size-B0
+ ;; CHECK-NEXT: (call $byn-split-outlined-B$big-size-B
+ ;; 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-$big-size-B
+ ;; We'll partially inline here.
+ (drop
+ (call $big-size-B
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $big-size-B
+ (i32.const 1)
+ )
+ )
+ )
+)
+;; CHECK: (func $byn-split-outlined-A$big-size-A (type $i32_=>_none) (param $x i32)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $byn-split-outlined-B$big-size-B (type $i32_=>_i32) (param $x i32) (result i32)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (unreachable)
+;; CHECK-NEXT: )