-
-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Fix; target feature inline always #155426
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
Open
Jamesbarford
wants to merge
4
commits into
rust-lang:main
Choose a base branch
from
Jamesbarford:fix/target-feature-inline-always
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
58ce116
Update logic for handling target feature inline always mismatches
Jamesbarford df654be
Update diagnostic messages
Jamesbarford b269cc3
Update and create new tests
Jamesbarford 2414698
Add a `noinline` attribute at the callsite when we bail on adding `al…
Jamesbarford File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
To echo the problem @tmiasko raised in #145574, which I now finally understand: LLVM can move a call site to another function via inlining. So whatever reasoning we do here based on the attributes of the current caller is largely pointless since we don't know the attributes of the actual caller that this call may eventually end up in.
View changes since the review
Uh oh!
There was an error while loading. Please reload this page.
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.
Actually constructing a counterexample could be tricky because LLVM seems to inline
alwaysinlinefunctions first, so I have not managed to get it to move around analwaysinlinecall site. But that seems like a fragile property to rely on.The entire approach to target_feature_inline_always seems very unprincipled to me. We have no solid writeup of when
alwaysinlineis safe to put on a call site in LLVM IR, which means we don't even know the exact property rustc has to check before adding that attribute. This feature needs less "let's implement something and see if it works" (an approach that does not work for optimizations where the test suite is never even close to being able to find all bugs) and more "let's figure out a principled argument for why what we want to do could be correct".Uh oh!
There was an error while loading. Please reload this page.
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.
Right now, based on the most recent example by @tmiasko , I am not convinced that there even exists a sound way to use
alwaysinlinein LLVM IR for calls to functions with extra target features. Even the most conservative option where we require full feature equality seems to go wrong:Imagine LLVM first inlines
gintoh(sound because none of the calls inghas a target-feature-dependent ABI). Then LLVM changesito receive the argument by-value (sound because the only caller and callee have the same target features). We end up with:Now we inline
f:Now the caller and callee disagree on the ABI. Oopsie...
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 think I understand your point, but I may still be missing something.
I took your example, removed
#[inline(always)], and compared the output here: https://godbolt.org/z/8xs8Gfjbj. In both cases, whetherfhas#[inline(always)]or not,hends up directly callingi.I also tried removing all
pubdeclarations except onh: https://godbolt.org/z/onszcGPPc. That produces the same output both with and without#[inline(always)]onf.So from these examples, it does not seem like
#[inline(always)]is making the situation less safe. The underlying problem looks like an LLVM issue that exists regardless, rather than something introduced by the attribute itself.What kind of writeup would you want here? The current code comments are probably not enough, but I want to make sure I understand what is missing and where you would expect it to live.
The rule this PR is trying to encode is:
If both conditions hold, we apply
alwaysinlineat the call site. If either condition fails, we instead applynoinline, which prevents further inlining at that call site.The goal of this PR is not to solve every inlining-related issue. It is to make problematic cases of the specific
#[inline(always)]attribute usage easier to detect and safer to handle.For example, in the
cx16issue (load_internalas the callee), condition 2 would fail ifload_internalwere marked#[inline(always)]. In that case, this PR would emit a warning and applynoinlineat the call site, preventing further LLVM inlining there. That is safer than today's#[inline]behaviour, because we both surface the problem to the user and block additional inlining at that call site. At the same time, becausealwaysinlineis no longer attached to the function definition itself likeinlinehint, valid uses ofload_internalcan still be inlined.Likewise, in the example from the tracking issue, which is the basis for this test case. Condition 1 fails, because the caller affects the callee's ABI.
Uh oh!
There was an error while loading. Please reload this page.
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.
Yes, LLVM today happens to do inlining in a different order that avoids the problem, at least for this particular example. But I would not want to bet the soundness of the language on LLVM always sticking to this inlining order.
That's why I described the expected order of applied optimizations in my example. You have to apply the optimizations manually to confirm (or reject) my reasoning. But as long as it is permitted for LLVM to do these optimizations in this order, that means the code we generate is unsound.
Uh oh!
There was an error while loading. Please reload this page.
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 would like to see an argument for why the rules you came up with are enough to guarantee that we avoid LLVM's soundness bugs around
alwaysinline. That's the main goal here, after all:alwaysinlineis fundamentally busted, but this feature is trying to use it soundly somehow. So far I am not convinced that is even possible. The example above shows, I think, that even with the very strict rule "caller and callee must have the exact same target features",alwaysinlineis still unsound.You cannot argue for the correctness of those rules by pointing at some examples. You can only make such an argument by saying: here's how inlining in LLVM works, and here is why under every possible inlining choice LLVM might make on any program, the result will be sound. Usually we leave that work to LLVM, but the entire premise of this approach is that we do the work ourselves because LLVM doesn't do it properly. That's fundamentally very hard (much harder than doing it in LLVM) as it requires reasoning "behind LLVM's back", so we need to be very careful and deliberate.