Skip to content

SILGen: Move error values into indirect error returns in proper order with cleanups. #73092

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

Merged

Conversation

jckarter
Copy link
Contributor

There's an unfortunate layering difference in the cleanup order between address-only and loadable error values during catch pattern matching: for address-only values, the value is copied into a temporary stack slot, and the stack slot is cleaned up on exit from the pattern match, meaning the value must be moved into the error return slot on the "no catch" case before cleanups run. But if it's a loadable value, then we borrow it for the duration of the switch, and the borrow is released during cleanup on exit from the pattern match, so the value must be forwarded after running cleanups.

The way the code is structured, it handles these cases properly when the convention of the function being emitted is in sync with the fundamental properties of the error type (when the error type is loadable and the error return is by value, or when the error type is address-only and the error return is indirect, in other words). But when a closure literal with a loadable error type is emitted in an argument context that expects a function with an indirect error return, we would try to forward the loadable error value into the error return slot while a borrow is still active on it, leading to verifier errors. Defer forwarding the value into memory until after cleanups are popped, fixing rdar://126576356.

A tidier solution might be to always emit the function body to use a bbarg on the throw block to pass the error value from the body emission to the epilog when the type is loadable, deferring the move into memory to the epilog block. This would make the right behavior fall out of the existing implementation, but would require a bit more invasive changes (pretty much everywhere that checks IndirectErrorReturn would need to check a different-tracked AddressOnlyErrorType bit instead or in addition). This change is more localized.

… with cleanups.

There's an unfortunate layering difference in the cleanup order between address-only
and loadable error values during `catch` pattern matching: for address-only values,
the value is copied into a temporary stack slot, and the stack slot is cleaned up
on exit from the pattern match, meaning the value must be moved into the error return
slot on the "no catch" case before cleanups run. But if it's a loadable value, then
we borrow it for the duration of the switch, and the borrow is released during cleanup
on exit from the pattern match, so the value must be forwarded after running cleanups.

The way the code is structured, it handles these cases properly when the convention of
the function being emitted is in sync with the fundamental properties of the error type
(when the error type is loadable and the error return is by value, or when the error
type is address-only and the error return is indirect, in other words). But when
a closure literal with a loadable error type is emitted in an argument context that
expects a function with an indirect error return, we would try to forward the loadable
error value into the error return slot while a borrow is still active on it, leading
to verifier errors. Defer forwarding the value into memory until after cleanups are
popped, fixing rdar://126576356.

A tidier solution might be to always emit the function body to use a bbarg on the
throw block to pass the error value from the body emission to the epilog when the
type is loadable, deferring the move into memory to the epilog block. This would
make the right behavior fall out of the existing implementation, but would require
a bit more invasive changes (pretty much everywhere that checks IndirectErrorReturn
would need to check a different-tracked AddressOnlyErrorType bit instead or in
addition). This change is more localized.
@jckarter jckarter requested a review from DougGregor April 17, 2024 21:37
@jckarter
Copy link
Contributor Author

@swift-ci Please test

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.

1 participant