Skip to content

Conversation

@kripken
Copy link
Member

@kripken kripken commented Dec 5, 2024

If we see a StructGet with no content (the type it reads from has no writes)
then we can make it unreachable. The old code literally just changed the type
to unreachable, which would later work out with refinalization - but only if
the StructGet's ref was unreachable. But it is possible for this situation to
occur without that, and if so, this hit the validation error "can't have an
unreachable node without an unreachable child".

To fix this, merge all code paths that handle "impossible" situations, which
simplifies things, and add this situation.

@kripken kripken requested a review from tlively December 5, 2024 22:36
@kripken
Copy link
Member Author

kripken commented Dec 5, 2024

(diff is smaller without whitespace)

Comment on lines 1615 to 1620
;; A struct.new, so that we have a field to refine.
(drop
(struct.new $optimizable
(ref.null func)
)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this affect the rest of the test given that $optimizable and $never are not related types? Is it because the pass returns early if there are no fields to refine, so we wouldn't hit the bug without this? If so, it would be good to mention that explicitly in the comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was it. Done.

Comment on lines 1638 to 1640
(func $export (export "export") (result (ref $never))
;; Make the type $never public (if it were private, we'd optimize in a
;; different way that avoids the bug that this tests for).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's usually simpler to export a global with the public type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// so we should refinalize. However, we will be refinalizing later
// anyhow in updateTypes, so there is no need.
if (curr->ref->type.isNull() || newFieldType == Type::unreachable ||
!Type::isSubType(newFieldType, curr->type)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test that tickles this last !Type::isSubType(...) condition? It is surprising that that could ever happen.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the relevant test:

The test hits an internal assertion without this condition.

@kripken
Copy link
Member Author

kripken commented Dec 9, 2024

@tlively PTAL at the last commit: that fixes a bug the fuzzer found. We were not actually noting content for struct.new_default, so we thought fields only ever assigned that way are never-written. This was not noticeable until this PR because we didn't use that information fully.

@kripken kripken merged commit e9f693d into WebAssembly:main Dec 9, 2024
13 checks passed
@kripken kripken deleted the tr.ref branch December 9, 2024 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants