When eliminating a tail call, we modify the values of the arguments.
Therefore, if the byval parameter has a readonly attribute, we have to remove it. It is safe because,
from the perspective of a caller, the byval parameter is always treated as "readonly," even if the readonly attribute is removed.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
LGTM, but maybe wait a day before committing in case somebody disagrees with this approach.
I would also recommend to add a PhaseOrdering test for https://github.com/llvm/llvm-project/issues/64289. This miscompile here is due to an interaction of multiple passes, so I think it's worthwhile to check the full optimization pipeline.
Do you think we need to add someone else to review it?
TailRecursionElimination.cpp has had very few non-NFC changes recently.
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 | probably, instead of removing readonly attribute we could stop generating it? Otherwise it will be presented or not depending of whether tail recursion elimination is happened. byval(<ty>) This indicates that the pointer parameter should really be passed by value to the function. The attribute implies that a hidden copy of the pointee is made between the caller and the callee, so the callee is unable to modify the value in the caller. The hidden copy protects original value from modifications done by callee. Having readonly attribute for hidden copy, probably, redundant. |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 | Do you mean don't add readonly at PostOrderFunctionAttrsPass? |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 | if readonly is important then we probably should not do tail recursion elimination for readonly + byval as we will write into the readonly data. if it is not important then it should be safe to not set this flag? |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 | Marking a byval pointer readonly isn't very useful; MemorySSA can easily analyze a byval argument without the marking in almost all cases. I would tend towards saying we shouldn't add readonly markings to byval values, if only to reduce the chance of this sort of confusion. But that doesn't really impact this patch: unless we actually make "readonly byval" illegal, TCE needs to handle it anyway. |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 |
It is possible to perform a transformation in a way that prevents another, larger optimization.
Using readonly we can avoid reusing MemorySSA analysis. This should reduce compilation time. And that covers all cases.
| |
683 |
|
In my mind, this is a worse case of mis-compilation.
With the current discussion, we have three methods to address it:
- Remove the readonly attribute, which is safe with byval.
- Do not add the readonly attribute to byval.
- Prevent the tail recursion elimination.
First, all three methods can solve this problem.
But I prefer the first one, and I think there is less opportunity to prevent other optimizations.
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
683 |
So I think I'd go ahead and submit this patch to fix the miscompilation. If anyone has other ideas they can submit a new patch. |
probably, instead of removing readonly attribute we could stop generating it? Otherwise it will be presented or not depending of whether tail recursion elimination is happened.
The hidden copy protects original value from modifications done by callee. Having readonly attribute for hidden copy, probably, redundant.