-
Notifications
You must be signed in to change notification settings - Fork 787
Handle try in Flatten pass #2567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fdbbd74
834155a
d58252e
574121c
8fb0c89
111175b
503d2bb
3725b1b
d1e6f9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ | |
|
||
#include <ir/branch-utils.h> | ||
#include <ir/effects.h> | ||
#include <ir/eh-utils.h> | ||
#include <ir/flat.h> | ||
#include <ir/properties.h> | ||
#include <ir/type-updating.h> | ||
|
@@ -192,6 +193,37 @@ struct Flatten | |
loop->finalize(); | ||
replaceCurrent(rep); | ||
|
||
} else if (auto* tryy = curr->dynCast<Try>()) { | ||
// remove a try value | ||
Expression* rep = tryy; | ||
auto* originalBody = tryy->body; | ||
std::vector<Expression*> originalCatchBodies(tryy->catchBodies.begin(), | ||
tryy->catchBodies.end()); | ||
auto type = tryy->type; | ||
if (type.isConcrete()) { | ||
Index temp = builder.addVar(getFunction(), type); | ||
if (tryy->body->type.isConcrete()) { | ||
tryy->body = builder.makeLocalSet(temp, tryy->body); | ||
} | ||
for (Index i = 0; i < tryy->catchBodies.size(); i++) { | ||
if (tryy->catchBodies[i]->type.isConcrete()) { | ||
tryy->catchBodies[i] = | ||
builder.makeLocalSet(temp, tryy->catchBodies[i]); | ||
} | ||
} | ||
// and we leave just a get of the value | ||
rep = builder.makeLocalGet(temp, type); | ||
// the whole try is now a prelude | ||
ourPreludes.push_back(tryy); | ||
} | ||
tryy->body = getPreludesWithExpression(originalBody, tryy->body); | ||
for (Index i = 0; i < tryy->catchBodies.size(); i++) { | ||
tryy->catchBodies[i] = getPreludesWithExpression( | ||
originalCatchBodies[i], tryy->catchBodies[i]); | ||
} | ||
tryy->finalize(); | ||
replaceCurrent(rep); | ||
|
||
} else { | ||
WASM_UNREACHABLE("unexpected expr type"); | ||
} | ||
|
@@ -347,6 +379,10 @@ struct Flatten | |
<< type; | ||
} | ||
} | ||
|
||
// Flatten can generate blocks within 'catch', making pops invalid. Fix them | ||
// up. | ||
EHUtils::handleBlockNestedPops(curr, *getModule()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done at the beginning of |
||
} | ||
|
||
private: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. | ||
;; RUN: wasm-opt %s -all --flatten -S -o - | filecheck %s | ||
|
||
(module | ||
;; CHECK: (tag $e-i32 (param i32)) | ||
(tag $e-i32 (param i32)) | ||
;; CHECK: (tag $e-f32 (param f32)) | ||
(tag $e-f32 (param f32)) | ||
|
||
;; CHECK: (func $try_catch | ||
;; CHECK-NEXT: (local $x i32) | ||
;; CHECK-NEXT: (local $1 i32) | ||
;; CHECK-NEXT: (local $2 f32) | ||
;; CHECK-NEXT: (try $try | ||
;; CHECK-NEXT: (do | ||
;; CHECK-NEXT: (throw $e-i32 | ||
;; CHECK-NEXT: (i32.const 0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-i32 | ||
;; CHECK-NEXT: (local.set $1 | ||
;; CHECK-NEXT: (pop i32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (drop | ||
;; CHECK-NEXT: (local.get $1) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-f32 | ||
;; CHECK-NEXT: (local.set $2 | ||
;; CHECK-NEXT: (pop f32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (drop | ||
;; CHECK-NEXT: (local.get $2) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
(func $try_catch (local $x i32) | ||
;; Basic try-catch test | ||
(try | ||
(do | ||
(throw $e-i32 (i32.const 0)) | ||
) | ||
(catch $e-i32 | ||
(drop | ||
(pop i32) | ||
) | ||
) | ||
(catch $e-f32 | ||
(drop | ||
(pop f32) | ||
) | ||
) | ||
) | ||
) | ||
|
||
;; CHECK: (func $try_catch_pop_fixup | ||
;; CHECK-NEXT: (local $0 i32) | ||
;; CHECK-NEXT: (local $1 i32) | ||
;; CHECK-NEXT: (block $l0 | ||
;; CHECK-NEXT: (try $try | ||
;; CHECK-NEXT: (do | ||
;; CHECK-NEXT: (nop) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-i32 | ||
;; CHECK-NEXT: (local.set $1 | ||
;; CHECK-NEXT: (pop i32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (block | ||
;; CHECK-NEXT: (block | ||
;; CHECK-NEXT: (local.set $0 | ||
;; CHECK-NEXT: (local.get $1) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (drop | ||
;; CHECK-NEXT: (local.get $0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (br $l0) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
(func $try_catch_pop_fixup | ||
;; After --flatten, a block is created within the 'catch', which makes the | ||
;; pop's location invalid. This tests whether it is fixed up correctly after | ||
;; --flatten. | ||
(block $l0 | ||
(try | ||
(do) | ||
(catch $e-i32 | ||
(drop | ||
(pop i32) | ||
) | ||
(br $l0) | ||
) | ||
) | ||
) | ||
) | ||
|
||
;; CHECK: (func $try_catch_return_value (result i32) | ||
;; CHECK-NEXT: (local $0 i32) | ||
;; CHECK-NEXT: (local $1 i32) | ||
;; CHECK-NEXT: (local $2 i32) | ||
;; CHECK-NEXT: (try $try | ||
;; CHECK-NEXT: (do | ||
;; CHECK-NEXT: (local.set $1 | ||
;; CHECK-NEXT: (i32.const 0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-i32 | ||
;; CHECK-NEXT: (local.set $0 | ||
;; CHECK-NEXT: (pop i32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $1 | ||
;; CHECK-NEXT: (local.get $0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $2 | ||
;; CHECK-NEXT: (local.get $1) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (return | ||
;; CHECK-NEXT: (local.get $2) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
(func $try_catch_return_value (result i32) | ||
(try (result i32) | ||
(do | ||
(i32.const 0) | ||
) | ||
(catch $e-i32 | ||
(pop i32) | ||
) | ||
) | ||
) | ||
|
||
;; CHECK: (func $try_unreachable (result i32) | ||
;; CHECK-NEXT: (local $0 i32) | ||
;; CHECK-NEXT: (local $1 i32) | ||
;; CHECK-NEXT: (local $2 i32) | ||
;; CHECK-NEXT: (try $try | ||
;; CHECK-NEXT: (do | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-i32 | ||
;; CHECK-NEXT: (local.set $0 | ||
;; CHECK-NEXT: (pop i32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $1 | ||
;; CHECK-NEXT: (local.get $0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $2 | ||
;; CHECK-NEXT: (local.get $1) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (return | ||
;; CHECK-NEXT: (local.get $2) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
(func $try_unreachable (result i32) | ||
(try (result i32) | ||
(do | ||
(unreachable) | ||
) | ||
(catch $e-i32 | ||
(pop i32) | ||
) | ||
) | ||
) | ||
|
||
;; CHECK: (func $catch_unreachable (result i32) | ||
;; 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: (try $try | ||
;; CHECK-NEXT: (do | ||
;; CHECK-NEXT: (local.set $3 | ||
;; CHECK-NEXT: (i32.const 3) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (catch $e-i32 | ||
;; CHECK-NEXT: (local.set $5 | ||
;; CHECK-NEXT: (pop i32) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (block | ||
;; CHECK-NEXT: (block | ||
;; CHECK-NEXT: (local.set $0 | ||
;; CHECK-NEXT: (local.get $5) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (drop | ||
;; CHECK-NEXT: (local.get $0) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: (unreachable) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $2 | ||
;; CHECK-NEXT: (local.get $1) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $3 | ||
;; CHECK-NEXT: (local.get $2) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (local.set $4 | ||
;; CHECK-NEXT: (local.get $3) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: (return | ||
;; CHECK-NEXT: (local.get $4) | ||
;; CHECK-NEXT: ) | ||
;; CHECK-NEXT: ) | ||
(func $catch_unreachable (result i32) | ||
(try (result i32) | ||
(do | ||
(i32.const 3) | ||
) | ||
(catch $e-i32 | ||
(drop | ||
(pop i32) | ||
) | ||
(unreachable) | ||
) | ||
) | ||
) | ||
) | ||
kripken marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this if just skips adding a set if the body is unreachable, as a minor optimization? I don't think it's needed IIUC. And the catch bodies do not do this check which makes it a little surprising to see it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copy-pasted
if
's code here again:binaryen/src/passes/Flatten.cpp
Lines 152 to 159 in 3c1c8ae
I tried to remove these
if (iff->ifTrue->type.isConcrete())
andif (iff->ifFalse && iff->ifFalse->type.isConcrete())
to see if these also can be removed, but if I do that these test fail:I haven't checked why they fail (and I have to run for today), but I guess there can be cases these are necessary...? If that's the case I think I should also check the catch bodies too. I'll do that for the moment, and if they turn out not to be necessary, I will remove them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks. In that case let's check the catch bodies too, that seems most consistent with the rest of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's OK to remove these body checks (both in
if
andtry
); the reason those tests failed was not because they crashed, but because the filecheck results were different. As you said, this does a small optimization that prevents this form:But I think it's OK to leave them (both in
if
andtry
) anyway; whileFlatten
doesn't try to do any optimizations itself, it wouldn't hurt, and maybe the output will be a little easier to read...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgtm, this simple pattern doesn't add much code complexity and it does make the output smaller and simpler.