-
Notifications
You must be signed in to change notification settings - Fork 785
[EH] Make rethrow's target a try label #3568
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
Conversation
I was previously mistaken about `rethrow`'s argument rule and thought it only counted `catch`'s depth. But it turns out it follows the same rule with `delegate`'s label: the immediate argument is comptuted in the same way as branch labels, but it only can target `try` labels (semantically it targets that `try`'s corresponding `catch`); otherwise it will be a validation failure. Unlike `delegate`, `rethrow`'s label denotes not where to rethrow, but which exception to rethrow. For example, ```wasm try $l0 catch ($l0) try $l1 catch ($l1) rethrow $l0 ;; rethrow the exception caught by 'catch ($l0)' end end ``` Refer to this comment for the more detailed informal semantics: WebAssembly/exception-handling#146 (comment) This also reverses some of `delegateTarget` -> `exceptionTarget` changes done in WebAssembly#3562 in the validator. Label validation rules apply differently for `delegate` and `rethrow` for try-catch. For example, this is valid: ```wasm try $l0 try delegate $l0 catch ($l0) end ``` But this is NOT valid: ```wasm try $l0 catch ($l0) try delegate $l0 end ``` So `try`'s label should be used within try-catch range (not catch-end range) for `delegate`s. But for the `rethrow` the rule is different. For example, this is valid: ```wasm try $l0 catch ($l0) rethrow $l0 end ``` But this is NOT valid: ```wasm try $l0 rethrow $l0 catch ($l0) end ``` So the `try`'s label should be used within catch-end range instead.
Expression* | ||
SExpressionWasmBuilder::makeTryOrCatchBody(Element& s, Type type, bool isTry) { | ||
if (isTry && !elementStartsWith(s, "do")) { | ||
throw ParseException("invalid try do clause", s.line, s.col); | ||
} | ||
if (!isTry && !elementStartsWith(s, "catch") && | ||
!elementStartsWith(s, "catch_all")) { | ||
throw ParseException("invalid catch clause", s.line, s.col); | ||
} | ||
if (s.size() == 1) { // (do) / (catch) / (catch_all) without instructions | ||
return makeNop(); | ||
} | ||
auto ret = allocator.alloc<Block>(); | ||
for (size_t i = 1; i < s.size(); i++) { | ||
ret->list.push_back(parseExpression(s[i])); | ||
} | ||
if (ret->list.size() == 1) { | ||
return ret->list[0]; | ||
} | ||
ret->finalize(type); | ||
return ret; | ||
} | ||
|
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.
This method was already unused; it became unused in #3487 but wasn't deleted there.
;; This i32.add will be dce'd | ||
(i32.add | ||
(i32.const 0) | ||
(rethrow $l0) |
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.
This function had to change because now rethrow
should be within a catch
.
src/wasm/wasm-validator.cpp
Outdated
shouldBeTrue(delegateTargetNames.empty(), | ||
curr->body, | ||
"all named delegate targets must exist"); | ||
shouldBeTrue(rethrowTargetNames.empty(), | ||
curr->body, | ||
"all named rethrow targets must exist"); |
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.
Couldn't these only fail if there was a bug in the validator itself? If so, should these be asserts instead?
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.
Isn't that the case basically for all checking points within the validator? We already have similar lines:
binaryen/src/wasm/wasm-validator.cpp
Lines 2534 to 2538 in 1d3f578
shouldBeTrue( | |
breakInfos.empty(), curr->body, "all named break targets must exist"); | |
shouldBeTrue(exceptionTargetNames.empty(), | |
curr->body, | |
"all named delegate targets must exist"); |
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.
In other words, is it possible to construct an invalid Binaryen IR module that would trigger this error? (I would ask the same questions of any of the other checks we have)
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.
Yeah I think so. Some flawed pass can assign a block
label to delegate
, or even a random string (unlikely), but anyway I don't think it's impossible.
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.
But that would be caught by https://github.com/WebAssembly/binaryen/pull/3568/files#diff-2149be365b66a82038f1d3fa8f9fb1b4dcd40ab535c986f4bf0a4c37e669a1ceR2079-R2092, right? These checks are just checking that the target names were properly removed from their sets at the end of the corresponding scopes AFAICT, and it shouldn't be possible to create an invalid Binaryen module in which a try scope or catch scope never ends.
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.
Hmm, I think you're right. Then maybe I'll remove all those checks..?
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.
Removed.
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.
Oh no, you suggested to make them assertions... Will change them to assertions.
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.
LGTM besides those few comments.
I was previously mistaken about
rethrow
's argument rule and thoughtit only counted
catch
's depth. But it turns out it follows the samerule
delegate
's label: the immediate argument follows the same rule aswhen computing branch labels, but it only can target
try
labels(semantically it targets that
try
's correspondingcatch
); otherwiseit will be a validation failure. Unlike
delegate
,rethrow
's labeldenotes not where to rethrow, but which exception to rethrow. For
example,
Refer to this comment for the more detailed informal semantics:
WebAssembly/exception-handling#146 (comment)
This also reverts some of
delegateTarget
->exceptionTarget
changesdone in #3562 in the validator. Label validation rules apply differently
for
delegate
andrethrow
for try-catch. For example, this is valid:But this is NOT valid:
So
try
's label should be used within try-catch range (not catch-endrange) for
delegate
s.But for the
rethrow
the rule is different. For example, this is valid:But this is NOT valid:
So the
try
's label should be used within catch-end range instead.