-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Implement & use metadata on arguments in LLVM #50156
Comments
Nice! Thanks for filing this and being willing to mentor its implementation. |
cc #50157 which is much easier but strongly related and complementary. |
Mentoring instructions for the LLVM changes: Currently metadata can be attached to instructions, and attributes can be attached to both function arguments and parameters, as well as the return value. For metadata-on-arguments we'll draw inspiration from the APIs for both. The implementation can be simpler than both of those, though you may still want to look through it. For that, and in general for browsing LLVM code, I recommend https://code.woboq.org/llvm/llvm/ which offers go-to-definition, go-to-declaration, find-all-uses, etc. We don't need metadata on return values (you can already attach metadata to the call instruction, and this indeed already works for range metadata today). I also don't see a need for metadata on the values passed to a call (e.g. there also isn't range metadata on stores). So we restrict outselves to the formal parameters of the function definition -- confusingly called The APIs would be similar to what Metadata on instructions is implemented in the superclass Once that is done, passes need to be updated to query the new metadata when analyzing |
@rkruppe Should the |
I think in analogy with |
Would this change mean that attributes like |
Yes, I believe this would obsolete some attributes on formal parameters. For argument values at specific call sites you'd still use attributes, but I don't think callsite annotations are useful for |
Reviewing the list of attributes in the Language Reference, metadata-on-parameters could also obsolete Other optimization hints that could conceivably become metadata are |
I ran into an issue where I did a method call on a nil interface and it resulted in a HardFault. Luckily I quickly realized what was going on so I could fix it, but I think undefined behavior is definitely the wrong behavior in this case. This commit therefore changes such calls to cause a nil panic instead of introducing undefined behavior. This does have a code size impact. It's relatively minor, much lower than I expected. When comparing the before and after of the drivers smoke tests (probably the most representative sample available), I found that most did not change at all and those that did change, normally not more than 100 bytes (16 or 32 byte changes are typical). Right now the pattern is the following: switch typecode { case 1: call method 1 case 2: call method 2 default: nil panic } I also tried the following (in the hope that it would be easier to optimize), but it didn't really result in a code size reduction: switch typecode { case 1: call method 1 case 2: call method 2 case 0: nil panic default: unreachable } Some code got smaller, while other code (the majority) got bigger. Maybe this can be improved once range[1] is finally allowed[2] on function parameters, but it'll probably take a while before that is implemented. [1]: https://llvm.org/docs/LangRef.html#range-metadata [2]: rust-lang/rust#50156
I ran into an issue where I did a method call on a nil interface and it resulted in a HardFault. Luckily I quickly realized what was going on so I could fix it, but I think undefined behavior is definitely the wrong behavior in this case. This commit therefore changes such calls to cause a nil panic instead of introducing undefined behavior. This does have a code size impact. It's relatively minor, much lower than I expected. When comparing the before and after of the drivers smoke tests (probably the most representative sample available), I found that most did not change at all and those that did change, normally not more than 100 bytes (16 or 32 byte changes are typical). Right now the pattern is the following: switch typecode { case 1: call method 1 case 2: call method 2 default: nil panic } I also tried the following (in the hope that it would be easier to optimize), but it didn't really result in a code size reduction: switch typecode { case 1: call method 1 case 2: call method 2 case 0: nil panic default: unreachable } Some code got smaller, while other code (the majority) got bigger. Maybe this can be improved once range[1] is finally allowed[2] on function parameters, but it'll probably take a while before that is implemented. [1]: https://llvm.org/docs/LangRef.html#range-metadata [2]: rust-lang/rust#50156
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization. r? `@nikic`
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization. r? `@nikic`
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization. r? `@nikic`
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization. try-job: i686-gnu
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization.
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang#127513 closes rust-lang#50156 cc rust-lang#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization.
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang/rust#127513 closes rust-lang/rust#50156 cc rust-lang/rust#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization.
Add range attribute to scalar function results and arguments as LLVM 19 adds the range attribute this starts to use it for better optimization. hade been interesting to see a perf run with the rust-lang/rust#127513 closes rust-lang/rust#50156 cc rust-lang/rust#49572 shall be fixed but not possible to see as there is asserts that already trigger the optimization.
We use metadata attached to LLVM instructions to tell LLVM passes about the range of possible integers that a value can take on. This allows additional optimizations. For example, when loading from a
&NonZeroU32
, the LLVM IR looks like this:where
!33
refers to a module-level entry like this (usually located near the end of the module source code):This tells LLVM that it's UB if the load returns anything that is not in the range ">= 1, < 0" (with wrap-around, i.e. it includes all values except 0). This allows
x.get() != 0
(wherex: &NonZeroU32
) to be optimized totrue
, for example.Unfortunately LLVM does not have a way to attach this information to function arguments, which leads to missed optimizations (e.g. #49572, #49420 (comment)). Adding this capability has been discussed before on llvm-dev and received positively, but was never actually implemented. If we want to use this in Rust, we're likely going to have to implement (& upstream) it outselves.
This issue tracks:
cc @nox @eddyb
The text was updated successfully, but these errors were encountered: