diff options
author | Heejin Ahn <aheejin@gmail.com> | 2021-12-20 20:18:32 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-20 20:18:32 -0800 |
commit | 083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5 (patch) | |
tree | d3fbb726330a46bdabe80d05081418d0a3fd2d86 | |
parent | 80101484099e9451072eb8c10df69499031b3475 (diff) | |
download | binaryen-083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5.tar.gz binaryen-083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5.tar.bz2 binaryen-083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5.zip |
[EH] Handle nested pops after inlining (#4404)
Inlining creates additional `block`s at inlined call sites, which can be
inside a `catch`. For example:
```wast
(try
(do)
(catch $tag
(call $callee
(pop i32)
)
)
)
```
After inlining, this becomes
```wast
(try
(do)
(catch $tag
(block $__inlined_func$callee
(local.set $0
(pop i32) ;; Invalid!!
)
(nop)
)
)
)
```
Now the `pop` is nested in a `block`, which makes this invalid. This PR
runs `EHUtils::handleBlockNestedPops` at the end to assign the `pop` to
a local right after the `catch`, making the code valid again:
```wast
(try
(do)
(catch $tag
(local.set $new ;; New local to store `pop` result
(pop i32)
)
(block $__inlined_func$callee
(local.set $0
(local.get $new)
)
(nop)
)
)
)
```
-rw-r--r-- | src/passes/Inlining.cpp | 5 | ||||
-rw-r--r-- | test/lit/passes/inlining-eh.wast | 47 |
2 files changed, 49 insertions, 3 deletions
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index 018654882..e81649bd2 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -32,6 +32,7 @@ #include "ir/branch-utils.h" #include "ir/debug.h" +#include "ir/eh-utils.h" #include "ir/element-utils.h" #include "ir/literal-utils.h" #include "ir/module-utils.h" @@ -880,6 +881,10 @@ struct Inlining : public Pass { #endif for (auto* func : inlinedInto) { + EHUtils::handleBlockNestedPops(func, *module); + } + + for (auto* func : inlinedInto) { if (++iterationCounts[func->name] >= MaxIterationsForFunc) { return; } diff --git a/test/lit/passes/inlining-eh.wast b/test/lit/passes/inlining-eh.wast index ea49c9550..7d804805e 100644 --- a/test/lit/passes/inlining-eh.wast +++ b/test/lit/passes/inlining-eh.wast @@ -76,7 +76,7 @@ ) ;; --------------------------------------------------------------------------- - (func $callee (result i32) + (func $callee-a (result i32) (i32.const 42) ) @@ -90,7 +90,7 @@ ;; CHECK-NEXT: (delegate 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (block $__inlined_func$callee (result i32) + ;; CHECK-NEXT: (block $__inlined_func$callee-a (result i32) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -100,6 +100,47 @@ (do) (delegate 0) ) - (call $callee) + (call $callee-a) + ) + + + ;; --------------------------------------------------------------------------- + (func $callee-b (param i32)) + + ;; CHECK: (func $caller-with-pop + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try $try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $tag$0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$callee-b + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $caller-with-pop + (try + (do) + (catch $tag$0 + ;; After this $callee-b is inlined, there will be additional 'block's + ;; surrouding this 'pop', which makes its location invalid. We fix it by + ;; creating a new local to set the result of 'pop' and later use + ;; local.get to get the value within the inlined function body. + (call $callee-b + (pop i32) + ) + ) + ) ) ) |