Conversation
aheejin
left a comment
There was a problem hiding this comment.
Haven't finished reading the tests; some questions:
| // Avoid a type change by forcing to be non-nullable. In practice, this | ||
| // would have trapped before we get here, so this is just for |
There was a problem hiding this comment.
Why would this have trapped before we get here?
There was a problem hiding this comment.
We receive a null value from a place that has a non-nullable type. Somehow the null falls through there, which I think is only possible using ref.as_non_null which will trap on a null.
src/passes/OptimizeInstructions.cpp
Outdated
| // null, or it will return null if it is (and we have already handled the | ||
| // case of null before). |
There was a problem hiding this comment.
This sounds like this can't be null because we've already handled the case, but we've only handed it is ref.null, and this can still be null at runtime (as the comment in line 1335-1336 says). I think this comment is a little confusing.
There was a problem hiding this comment.
Good point, yeah, this is unclear. I'll rephrase.
src/ir/reorderer.h
Outdated
| // | ||
| // we generate something so that it is valid to write | ||
| // | ||
| // (Reorderer.second, Reorderer.first) |
There was a problem hiding this comment.
How about calling the new first first and the new second second? Because it is within Reorderer, I feel like it is less confusing for Reorderer's first should be the reordered first. To me it is harder to read the code assuming that this first should go second in the result and vice versa.
There was a problem hiding this comment.
Interesting, it's the opposite for me 😄
I think about it as "maybe the Reorderer will do nothing, and so Reorderer.second = second, which means the user wants to put that first when they reorder".
How about (Reorderer.reorderedFirst, Reorderer.reorderedSecond)? But that is still ambiguous...
There was a problem hiding this comment.
Hmm, my preference is in the order of first/second > reorderedFirst/reorderedSecond > second/first(the status quo), but if you feel this is more confusing, it is possible that this is just my intuition. I don't feel strongly, so let's leave it then.
src/passes/OptimizeInstructions.cpp
Outdated
| Reorderer reorderer( | ||
| curr->ref, curr->rtt, getFunction(), getModule(), passOptions); |
There was a problem hiding this comment.
Do we not need to check if they can be reordered effect-wise?
There was a problem hiding this comment.
That is what the Reorderer does - it potentially adds more code to handle that problem (it adds a local and saves the value here, keeping their order identical).
| ;; CHECK-NEXT: (ref.func $0) | ||
| ;; CHECK-NEXT: (rtt.canon $none_=>_i32) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (unreachable) |
There was a problem hiding this comment.
Do we need to change something in the comment below?
| ;; CHECK-NEXT: (drop | ||
| ;; CHECK-NEXT: (rtt.canon $none_=>_i32) | ||
| ;; CHECK-NEXT: ) | ||
| ;; CHECK-NEXT: (unreachable) |
There was a problem hiding this comment.
Here too; does the comment need changing?
There was a problem hiding this comment.
Sounds good, thanks, I'm updating these comments too.
src/ir/reorderer.h
Outdated
| // | ||
| // we generate something so that it is valid to write | ||
| // | ||
| // (Reorderer.second, Reorderer.first) |
There was a problem hiding this comment.
Hmm, my preference is in the order of first/second > reorderedFirst/reorderedSecond > second/first(the status quo), but if you feel this is more confusing, it is possible that this is just my intuition. I don't feel strongly, so let's leave it then.
| ;; CHECK: (type $array (array (mut i8))) | ||
|
|
||
| ;; CHECK: (type $B (struct (field i32) (field i32) (field f32))) | ||
|
|
There was a problem hiding this comment.
I wonder what the reason for this change is? Apparently $array and $B haven't changed...
There was a problem hiding this comment.
I think this is just because more uses were added, so it alters the order in which the types are emitted (more frequently used ones appear first).
| optimizer.walkFunction(func); | ||
| } | ||
|
|
||
| TypeUpdating::handleNonDefaultableLocals(func, *getModule()); |
There was a problem hiding this comment.
Would be helpful to add a comment that Reorderer can create locals so we need this
There was a problem hiding this comment.
Good point, adding a comment.
| curr->ref, getPassOptions(), getModule()->features); | ||
|
|
||
| // If the value is a null, it will just flow through, and we do not need the | ||
| // cast. However, if that would change the type, then things are less |
There was a problem hiding this comment.
If this expression is null and changes type from such as a struct to an array, would that possibly make the parent or grandparent expressions of this not validate? We fix nullability but what if the heaptype itself changes? Can Refinailize solve it?
There was a problem hiding this comment.
The code here is actually careful about the heap type, I'll improve the comment though.
Basically, we use the heap type of the RTT to create the null - that is the type on the outside.
| // Aside from the issue of type incompatibility as mentioned above, the | ||
| // cast can trap if the types *are* compatible but it happens to be the | ||
| // case at runtime that the value is not of the desired subtype. If we | ||
| // do not consider such traps possible, we can ignore that. Note, though, | ||
| // that we cannot do this if we cannot replace the current type with the | ||
| // reference's type. |
There was a problem hiding this comment.
Are traps possible even if ref's heap type is a subtype of rtt's heap type?
There was a problem hiding this comment.
Yes, this is an unfortunate property of rtts. E.g. (rtt.canon $x) != (rtt.sub $x (rtt.canon $x)).
This is bad for optimization, so we are hoping to prototype a more static approach soon, that would be as you describe basically.
There was a problem hiding this comment.
(rtt.canon $x) != (rtt.sub $x (rtt.canon $x))
😨
|
I think your intuition about the Reorderer is right, the more I think about it. I rewrote it to a method |
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: WebAssembly#4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in WebAssembly#4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: WebAssembly#4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in WebAssembly#4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: #4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in #4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: WebAssembly#4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in WebAssembly#4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: WebAssembly#4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in WebAssembly#4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
If the types are completely incompatible, we know the cast will fail. However,
ref.cast does allow a null to pass through, which makes it a little more
complicated.