-
Notifications
You must be signed in to change notification settings - Fork 786
LUBFinder: Update nulls #4302
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
base: main
Are you sure you want to change the base?
LUBFinder: Update nulls #4302
Conversation
Should be |
@tlively Do you mean to add a
The LUB is a type (and not a |
Yeah sorry I mistyped that comment originally. See the edit.
should be
|
Oh, right, thanks! Fixed. |
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.
Nice!
std::vector<Type> types; | ||
for (auto* null : updatableNulls) { | ||
types.push_back(null->type); | ||
} |
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.
Would it make sense to reuse note(Type)
here rather than introducing a new vector?
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 guess that could be more efficient. It would leave noticeable traces, though, if you look at the internals, even though they should be hidden from the outside.
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, fair enough. No reason to mess with longer-lived state here.
if (lub.get() == originalType) { | ||
return false; |
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 looks dangerous because get()
could update nulls even if it returns the original type, in which case we would have to return true
to refinalize. However, I think that if we get here, it must be that the lub is not originalType
because we would have returned already after one of the calls to processReturnValue
or processReturnType
. So can we turn this into an assertion that the lub is not equal to originalType
?
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, good point, this does look dangerous. I'll try to figure out something.
We can't assert here, because while you are right that the LUB is not originalType
, it might still become originalType
during the final computation - when we take into account nulls. But, that case is not dangerous to us here. It's annoying that the invariants that makes this safe are in the other file, which makes it hard to reason about safety here, which I'll think on.
(i32.const 2) | ||
(return (ref.null func)) | ||
) | ||
(ref.null func) |
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.
What if the nulls are not all the same type?
It is common in GC code to have stuff like this:
Nulls in wasm have a type, and if that initial null has say
anyref
thenbefore this PR we would keep the type of
x
asanyref
. However,while nulls have types, all null values are identical, and so we can
in fact change
x
's type to a nullable reference ofData
, by alsochanging the null's type. That is, the LUB of
can be
(ref null $Data)
if we update that null toThis PR adds that optimization to LUBFinder, and does the relevant
minor work to get that supported in the two passes using that
helper. The LUBFinder now tracks nulls and updates them at the
end if it makes sense to do so.
An annoyance is that
test/lit/passes/dae-gc-refine-return.wast
useda lot of nulls in that test file. But now the pass can optimize those. This
PR therefore adds blocks to wrap around them, which avoids changing
anything in the test there, though it is not a small diff sadly.