-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Inlining loses ABI-relevant target feature information at call
operations
#70563
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
Comments
This limitation in LLVM also prevents us from supporting (without unnecessary overhead) some pretty natural-looking Rust programs; see rust-lang/rust#132865 for details. |
@dtcxzyw @thesamesam Do you know who could help look into that? Thanks! |
@cor3ntin Context for your interest in this issue? From the Rust side, I believe our understanding of this problem space has largely evolved towards a preference to not have target features affect call ABI at all and instead be determined separately, at the module level (with certain ABIs requiring certain target features of course). This is roughly the design more modern targets follow. |
Except that AFAIK they will still fall back to a different ABI if a target feature for the requested ABI is missing. At least some of them emit a warning on stderr when that happens... But yeah, if all targets did this consistently and the warnings were made into hard errors then the inliner wouldn't have to be so careful around |
I was asked if I knew who to ping, no personal interest! Thanks for the answer |
The behavior of the
call
terminator is (currently) very sensitive to surrounding target features: e.g. on x86-64, when an argument of type<8 x float>
is passed, the exact way that argument will be passed depends on whether the function that contains thecall
has the AVX feature enabled.This means that the seemingly harmless operation of moving
call
from one function to another (e.g. via inlining) can lead to the behavior ofcall
changing, which is obviously bad! This is what happens in this example, where the uninlined program would behave as intended (caller and callee use matching ABI everywhere), but the program produced byclang -emit-llvm
callsno_target_feature
in a context where the AVX feature is available, thus breaking the call.Before:
After:
(We are running into this in Rust, where it causes code that should be completely fine to misbehave, so it's a critical issue for us that we'd like to fix.)
The obvious way to avoid this is to not do inlining when the target features differ between caller and callee, but that seems like a big hammer that leaves a lot of optimization potential on the table -- and indeed, if I understood correctly, the most recent attempt to enforce this for all kinds of inlining (including
alwaysinline
) quickly led to problems.An alternative would be to make
call
less context-dependent -- after all it's not the inlining itself that is the fundamental problem here, it's the fact thatcall
doesn't know the ABI it has to use for the callee, and then takes the caller target flags to fill that information gap -- a fragile heuristic, as the inlining issue shows. So I wonder if it wouldn't be possible to in fact still do the inlining, but end up with code likeIOW, the call terminator would stop relying on context clues (which are fragile since inlining changes the context), and instead it would carry an explicit annotation saying which target features should be considered when determining how arguments must be passed for this call. This approach would enable arbitrary inlining from "less feature" to "more feature" functions, provided the inliner takes care to equip the
call
instructions it is copying with the right annotation.(I don't know the LLVM internal data structures so I'm not sure how that annotation would best be represented, but LLVM already supports tons of annotations at various instructions so I hope adding one more isn't too hard.)
The text was updated successfully, but these errors were encountered: