-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[partial-apply-combiner] Fix partial_apply combiner implementation for indirect parameters #29365
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
[partial-apply-combiner] Fix partial_apply combiner implementation for indirect parameters #29365
Conversation
I do not believe that this code was ever correct and we just got lucky since in the vast majority of cases where this optimization kicks in we also eliminate the underlying partial apply/allocations. To make sure that we can properly test this code and make sure that we do not run into this problem again, I added an option to SILCombinerApplyVisitors that disables partial apply deletion so we can verify that we do not rely on eliminating the underlying partial apply for correctness. <rdar://problem/57855082> ---- To be more specific, partial_apply expects that it will take all of its input arguments at +1. This includes parameters that are passed @in or @in_guaranteed to the underlying function. Just to summarize the expected semantics are that: 1. If the underlying function takes the parameter @in then the partial_apply forwarder allows the underlying function to consume the argument. 2. If the underlying function takes the parameter @in_guaranteed then the partial_apply forwarder itself will destroy the underlying value. In terms of the partial_apply combiner this means that unless we can guarantee that the partial_apply is eliminated as a result of our transformation (which we can not givne the way this utility is written), we must introduce new copies of @in, @in_guaranteed partial applied arguments and lifetime extend those copies to the new direct apply that we are creating. In the case of the parameter being in_guaranteed we insert a destroy after that call site to ensure that our new copy is cleaned up. If we have an @in parameter then we allow the underlying function to clean up the value.
@swift-ci test |
@swift-ci benchmark |
…/closed over parameters.
1525bdc
to
1b807b8
Compare
@swift-ci test |
@swift-ci benchmark |
Performance: -O
Code size: -OPerformance: -Osize
Code size: -OsizePerformance: -OnoneCode size: -swiftlibsHow to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
Does this code handle the following situation correctly? (I left out alloc_stack and instead use a value representation)
This should be transformed to:
We need to handle the lifetime extension for the values captured in the closure's context separately from the handling of the convention in the partial_apply forwarder at the call site. |
Arnold and I spoke offline. I am pretty sure that this code assumes that the partial apply site and the actual call site are strongly control equivalent. This is not true if the partial apply site is out side of a loop and the call site are in a loop. That being said, the thing I am fixing in this PR is actually a case where we are wrong even in the strongly control equivalent case, so I am going to fix the weakly control equivalent case in a follow on PR. Assuming again that I am correct, in terms of propagating the fix/cherry-picking, I am going to get this one in and then have two follow on commits:
|
sgtm |
Found some more problems latent in the implementation here. I am going to split this into a few different PRs. Specifically:
I am going to fix the optimization by moving to a model where at the partial apply site, we only once create a copy of each partial applied argument (noting that alloc_stack, dealloc_stack are still created at the begin/end of the function and we would copy indirect arguments into those) and lifetime extend over all call sites. Then at each call site that we are eliminating, we create a new separate copy if the call site needs to consume the argument. Otherwise, we just pass in our original copy. |
I do not believe that this code was ever correct and we just got lucky since in
the vast majority of cases where this optimization kicks in we also eliminate
the underlying partial apply/allocations.
To make sure that we can properly test this code and make sure that we do not
run into this problem again, I added an option to SILCombinerApplyVisitors that
disables partial apply deletion so we can verify that we do not rely on
eliminating the underlying partial apply for correctness.
rdar://problem/57855082
To be more specific, partial_apply expects that it will take all of its input
arguments at +1. This includes parameters that are passed @in or @in_guaranteed
to the underlying function. Just to summarize the expected semantics are that:
If the underlying function takes the parameter @in then the partial_apply
forwarder allows the underlying function to consume the argument.
If the underlying function takes the parameter @in_guaranteed then the
partial_apply forwarder itself will destroy the underlying value.
In terms of the partial_apply combiner this means that unless we can guarantee
that the partial_apply is eliminated as a result of our transformation (which we
can not givne the way this utility is written), we must introduce new copies of
@in, @in_guaranteed partial applied arguments and lifetime extend those copies
to the new direct apply that we are creating. In the case of the parameter being
in_guaranteed we insert a destroy after that call site to ensure that our new
copy is cleaned up. If we have an @in parameter then we allow the underlying
function to clean up the value.
NOTE: the 2nd commit here just clarifies the documentation around partial_apply to make it much more explicit what the actual semantics are of partial_apply arguments.