diff options
-rw-r--r-- | src/passes/Inlining.cpp | 30 | ||||
-rw-r--r-- | test/lit/passes/inlining-optimizing_optimize-level=3.wast | 6 | ||||
-rw-r--r-- | test/lit/passes/inlining_optimize-level=3.wast | 43 |
3 files changed, 48 insertions, 31 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index 30bb5c23f..cbee7e77f 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -446,11 +446,11 @@ struct Updater : public TryDepthWalker<Updater> { }; // Core inlining logic. Modifies the outside function (adding locals as -// needed). -static void doInlining(Module* module, - Function* into, - const InliningAction& action, - PassOptions& options) { +// needed) by copying the inlined code into it. +static void doCodeInlining(Module* module, + Function* into, + const InliningAction& action, + PassOptions& options) { Function* from = action.contents; auto* call = (*action.callSite)->cast<Call>(); @@ -622,6 +622,12 @@ static void doInlining(Module* module, } *action.callSite = builder.makeSequence(old, builder.makeUnreachable()); } +} + +// Updates the outer function after we inline into it. This is a general +// operation that does not depend on what we inlined, it just makes sure that we +// refinalize everything, have no duplicate break labels, etc. +static void updateAfterInlining(Module* module, Function* into) { // Anything we inlined into may now have non-unique label names, fix it up. // Note that we must do this before refinalization, as otherwise duplicate // block labels can lead to errors (the IR must be valid before we @@ -635,6 +641,14 @@ static void doInlining(Module* module, TypeUpdating::handleNonDefaultableLocals(into, *module); } +static void doInlining(Module* module, + Function* into, + const InliningAction& action, + PassOptions& options) { + doCodeInlining(module, into, action, options); + updateAfterInlining(module, into); +} + // A map of function names to the inlining actions we've decided to actually // perform in them. using ChosenActions = std::unordered_map<Name, std::vector<InliningAction>>; @@ -657,9 +671,13 @@ struct DoInlining : public Pass { assert(iter != chosenActions.end()); const auto& actions = iter->second; assert(!actions.empty()); + + // Inline all the code first, then update func once at the end (which saves + // e.g. running ReFinalize after each action, of which there might be many). for (auto action : actions) { - doInlining(module, func, action, getPassOptions()); + doCodeInlining(module, func, action, getPassOptions()); } + updateAfterInlining(module, func); } private: diff --git a/test/lit/passes/inlining-optimizing_optimize-level=3.wast b/test/lit/passes/inlining-optimizing_optimize-level=3.wast index fdfbf7b35..f6df73056 100644 --- a/test/lit/passes/inlining-optimizing_optimize-level=3.wast +++ b/test/lit/passes/inlining-optimizing_optimize-level=3.wast @@ -8820,7 +8820,7 @@ ;; CHECK-NEXT: (br $__rjti$8) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block $label$break$L8 - ;; CHECK-NEXT: (block $__rjti$20 + ;; CHECK-NEXT: (block $__rjti$23 ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (local.tee $9 @@ -8845,7 +8845,7 @@ ;; CHECK-NEXT: (local.get $5) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (loop $while-in5 - ;; CHECK-NEXT: (br_if $__rjti$20 + ;; CHECK-NEXT: (br_if $__rjti$23 ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.load8_u ;; CHECK-NEXT: (local.get $8) @@ -8890,7 +8890,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br_if $__rjti$20 + ;; CHECK-NEXT: (br_if $__rjti$23 ;; CHECK-NEXT: (local.get $9) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $9 diff --git a/test/lit/passes/inlining_optimize-level=3.wast b/test/lit/passes/inlining_optimize-level=3.wast index 1064ba04d..ab9386533 100644 --- a/test/lit/passes/inlining_optimize-level=3.wast +++ b/test/lit/passes/inlining_optimize-level=3.wast @@ -503,23 +503,6 @@ ;; (That avoids possible validation problems, and maximizes DCE.) To keep it ;; unreachable we'll add an unreachable instruction after the inlined code. (module - ;; CHECK: (type $0 (func (param f32))) - - ;; CHECK: (type $1 (func)) - - ;; CHECK: (func $A (param $0 f32) - ;; CHECK-NEXT: (local $1 f32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result f32) - ;; CHECK-NEXT: (block $__inlined_func$C (result f32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) (func $A (param $0 f32) (drop (call $C @@ -527,12 +510,16 @@ ) ) ) + ;; CHECK: (type $0 (func)) + ;; CHECK: (func $B ;; CHECK-NEXT: (local $0 f32) - ;; CHECK-NEXT: (call $A - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local $1 f32) + ;; CHECK-NEXT: (local $2 f32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$A$3 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (block (result f32) ;; CHECK-NEXT: (block $__inlined_func$C$2 (result f32) ;; CHECK-NEXT: (local.tee $0 ;; CHECK-NEXT: (block @@ -544,7 +531,19 @@ ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (f32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result f32) + ;; CHECK-NEXT: (block $__inlined_func$C (result f32) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) |