diff options
author | Alon Zakai <azakai@google.com> | 2023-05-10 13:33:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-10 13:33:50 -0700 |
commit | b1dba91a6bf881b5bfa68da7bb56bbb3bbf90a0a (patch) | |
tree | f3dc4097e70742aa09c2965b99529f95f3a08876 | |
parent | c5c223943770412b2ebd7d9f23fce8c11cf5982e (diff) | |
download | binaryen-b1dba91a6bf881b5bfa68da7bb56bbb3bbf90a0a.tar.gz binaryen-b1dba91a6bf881b5bfa68da7bb56bbb3bbf90a0a.tar.bz2 binaryen-b1dba91a6bf881b5bfa68da7bb56bbb3bbf90a0a.zip |
Gate all partial inlining behind the partial-inlining-ifs flag (#5710)
#4191 meant to do that, I think, but only did so for "pattern B". This does it
for all patterns, and adds assertions.
In theory this could regress code that benefits from partial inlining of
"pattern A" (since this PR stops doing it by default), but I did not see a significant
difference on any benchmarks, and it is easy to re-enable that behavior by
doing --partial-inlining-ifs=1.
-rw-r--r-- | src/passes/Inlining.cpp | 16 | ||||
-rw-r--r-- | test/lit/passes/inlining_splitting_basics.wast | 207 |
2 files changed, 217 insertions, 6 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index 229842e12..3326b7370 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -57,7 +57,7 @@ enum class InliningMode { // This function cannot be inlinined in any way. Uninlineable, // This function can be inlined fully, that is, normally: the entire function - // can be inlined. This is in contrast to partial inlining, see below. + // can be inlined. This is in contrast to split/partial inlining, see below. Full, // This function cannot be inlined normally, but we can use split inlining, // using pattern "A" or "B" (see below). @@ -611,6 +611,9 @@ struct FunctionSplitter { // split on very specific patterns we believe are worth handling in that // manner). InliningMode getSplitDrivenInliningMode(Function* func, FunctionInfo& info) { + const Index MaxIfs = options.inlining.partialInliningIfs; + assert(MaxIfs > 0); + auto* body = func->body; // If the body is a block, and we have breaks to that block, then we cannot @@ -692,7 +695,6 @@ struct FunctionSplitter { // without an else. // Find the number of ifs. - const Index MaxIfs = options.inlining.partialInliningIfs; Index numIfs = 0; while (getIf(body, numIfs) && numIfs <= MaxIfs) { numIfs++; @@ -847,6 +849,7 @@ private: Function* inlineable = copyFunction(func, "inlineable-B"); const Index MaxIfs = options.inlining.partialInliningIfs; + assert(MaxIfs > 0); // The inlineable function should only have the ifs, which will call the // outlined heavy work. @@ -1066,10 +1069,11 @@ struct Inlining : public Pass { } // When optimizing heavily for size, we may potentially split functions in - // order to inline parts of them. - if (getPassOptions().optimizeLevel >= 3 && !getPassOptions().shrinkLevel) { - functionSplitter = - std::make_unique<FunctionSplitter>(module, getPassOptions()); + // order to inline parts of them, if partialInliningIfs is enabled. + auto& options = getPassOptions(); + if (options.optimizeLevel >= 3 && !options.shrinkLevel && + options.inlining.partialInliningIfs) { + functionSplitter = std::make_unique<FunctionSplitter>(module, options); } } diff --git a/test/lit/passes/inlining_splitting_basics.wast b/test/lit/passes/inlining_splitting_basics.wast new file mode 100644 index 000000000..443686101 --- /dev/null +++ b/test/lit/passes/inlining_splitting_basics.wast @@ -0,0 +1,207 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Test code that can be partially inlined, with and without that option. +;; +;; This is similar to inlining_splitting.wast but focuses on testing that we +;; only partially inline when the commandline flag is provided. In both of the +;; $call-* functions below, we should partially inline when that flag is set. + +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --all-features -S -o - | filecheck %s --check-prefix NORMAL_ +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --partial-inlining-ifs=1 --all-features -S -o - | filecheck %s --check-prefix PARTIAL + +(module + ;; NORMAL_: (type $none_=>_none (func)) + + ;; NORMAL_: (type $i32_=>_none (func (param i32))) + + ;; NORMAL_: (type $i32_=>_i32 (func (param i32) (result i32))) + + ;; NORMAL_: (import "a" "b" (func $import)) + ;; PARTIAL: (type $none_=>_none (func)) + + ;; PARTIAL: (type $i32_=>_none (func (param i32))) + + ;; PARTIAL: (type $i32_=>_i32 (func (param i32) (result i32))) + + ;; PARTIAL: (import "a" "b" (func $import)) + (import "a" "b" (func $import)) + + ;; Pattern A: functions beginning with + ;; + ;; if (simple) return; + + ;; NORMAL_: (func $pattern-A (type $i32_=>_none) (param $x i32) + ;; NORMAL_-NEXT: (if + ;; NORMAL_-NEXT: (local.get $x) + ;; NORMAL_-NEXT: (return) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: (loop $l + ;; NORMAL_-NEXT: (call $import) + ;; NORMAL_-NEXT: (br $l) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + (func $pattern-A (param $x i32) + (if + (local.get $x) + (return) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; NORMAL_: (func $call-pattern-A (type $none_=>_none) + ;; NORMAL_-NEXT: (call $pattern-A + ;; NORMAL_-NEXT: (i32.const 1) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: (call $pattern-A + ;; NORMAL_-NEXT: (i32.const 2) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + ;; PARTIAL: (func $call-pattern-A (type $none_=>_none) + ;; PARTIAL-NEXT: (local $0 i32) + ;; PARTIAL-NEXT: (local $1 i32) + ;; PARTIAL-NEXT: (block + ;; PARTIAL-NEXT: (block $__inlined_func$byn-split-inlineable-A$pattern-A + ;; PARTIAL-NEXT: (local.set $0 + ;; PARTIAL-NEXT: (i32.const 1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (if + ;; PARTIAL-NEXT: (i32.eqz + ;; PARTIAL-NEXT: (local.get $0) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (call $byn-split-outlined-A$pattern-A + ;; PARTIAL-NEXT: (local.get $0) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (block + ;; PARTIAL-NEXT: (block $__inlined_func$byn-split-inlineable-A$pattern-A$1 + ;; PARTIAL-NEXT: (local.set $1 + ;; PARTIAL-NEXT: (i32.const 2) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (if + ;; PARTIAL-NEXT: (i32.eqz + ;; PARTIAL-NEXT: (local.get $1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (call $byn-split-outlined-A$pattern-A + ;; PARTIAL-NEXT: (local.get $1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + (func $call-pattern-A + (call $pattern-A (i32.const 1)) + (call $pattern-A (i32.const 2)) + ) + + ;; Pattern B: functions containing + ;; + ;; if (simple1) heavy-work-that-is-unreachable; + ;; if (simple..) heavy-work-that-is-unreachable; + ;; simplek + + ;; NORMAL_: (func $pattern-B (type $i32_=>_i32) (param $x i32) (result i32) + ;; NORMAL_-NEXT: (if + ;; NORMAL_-NEXT: (i32.eqz + ;; NORMAL_-NEXT: (local.get $x) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: (block + ;; NORMAL_-NEXT: (call $import) + ;; NORMAL_-NEXT: (unreachable) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: (local.get $x) + ;; NORMAL_-NEXT: ) + (func $pattern-B (param $x i32) (result i32) + (if + (i32.eqz + (local.get $x) + ) + (block + (call $import) + (unreachable) + ) + ) + (local.get $x) + ) + + ;; NORMAL_: (func $call-pattern-B (type $none_=>_none) + ;; NORMAL_-NEXT: (drop + ;; NORMAL_-NEXT: (call $pattern-B + ;; NORMAL_-NEXT: (i32.const 1) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: (drop + ;; NORMAL_-NEXT: (call $pattern-B + ;; NORMAL_-NEXT: (i32.const 2) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + ;; NORMAL_-NEXT: ) + ;; PARTIAL: (func $call-pattern-B (type $none_=>_none) + ;; PARTIAL-NEXT: (local $0 i32) + ;; PARTIAL-NEXT: (local $1 i32) + ;; PARTIAL-NEXT: (drop + ;; PARTIAL-NEXT: (block (result i32) + ;; PARTIAL-NEXT: (block $__inlined_func$byn-split-inlineable-B$pattern-B$2 (result i32) + ;; PARTIAL-NEXT: (local.set $0 + ;; PARTIAL-NEXT: (i32.const 1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (block (result i32) + ;; PARTIAL-NEXT: (if + ;; PARTIAL-NEXT: (i32.eqz + ;; PARTIAL-NEXT: (local.get $0) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (br $__inlined_func$byn-split-inlineable-B$pattern-B$2 + ;; PARTIAL-NEXT: (call $byn-split-outlined-B$pattern-B + ;; PARTIAL-NEXT: (local.get $0) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (local.get $0) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (drop + ;; PARTIAL-NEXT: (block (result i32) + ;; PARTIAL-NEXT: (block $__inlined_func$byn-split-inlineable-B$pattern-B$3 (result i32) + ;; PARTIAL-NEXT: (local.set $1 + ;; PARTIAL-NEXT: (i32.const 2) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (block (result i32) + ;; PARTIAL-NEXT: (if + ;; PARTIAL-NEXT: (i32.eqz + ;; PARTIAL-NEXT: (local.get $1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (br $__inlined_func$byn-split-inlineable-B$pattern-B$3 + ;; PARTIAL-NEXT: (call $byn-split-outlined-B$pattern-B + ;; PARTIAL-NEXT: (local.get $1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: (local.get $1) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + ;; PARTIAL-NEXT: ) + (func $call-pattern-B + (drop (call $pattern-B (i32.const 1))) + (drop (call $pattern-B (i32.const 2))) + ) +) +;; PARTIAL: (func $byn-split-outlined-A$pattern-A (type $i32_=>_none) (param $x i32) +;; PARTIAL-NEXT: (loop $l +;; PARTIAL-NEXT: (call $import) +;; PARTIAL-NEXT: (br $l) +;; PARTIAL-NEXT: ) +;; PARTIAL-NEXT: ) + +;; PARTIAL: (func $byn-split-outlined-B$pattern-B (type $i32_=>_i32) (param $x i32) (result i32) +;; PARTIAL-NEXT: (call $import) +;; PARTIAL-NEXT: (unreachable) +;; PARTIAL-NEXT: ) |