-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Runtime code is generated for functions only used in const eval #119214
Comments
Note that adding |
This looks like another instance of #116505, meaning that depending on some heuristics leaf functions are only codegenned in cgu that need them. Since check1 and check2 are not called, they are not seen. This is good for optimizations but quite unfortunate for inspecting asm on Compiler Explorer. Adding |
I think there are multiple things happening here. I'll try to explain what I'm seeing.
That's not possible, those functions both take runtime parameters. I'm presuming you mean "use a compiletime-evaluated struct value" instead?
This is the cross-crate-inlining effect; I've been mulling over ideas to improve the situation, but this is not a bug. It's very unfortunate though. compiler-explorer/compiler-explorer#5782
This I think is a bug. I've made a smaller example here: https://godbolt.org/z/GoEa8jc5f and I'm going to look into it... |
The reproducer is this: pub struct Thing;
impl Thing {
const fn new(panic: bool) -> Self {
if panic {
panic!()
}
Thing
}
}
const T: Thing = Thing::new(false);
pub fn oof() -> Thing {
T
} Since automatic cross-crate-inlining landed, the assembly generated for this crate contains The change here is that
But to reachability analysis, this function has now made the initializer of that So cross-crate-inlining turns the surface area of this crate from "a symbol called So from one perspective, this is sensible. But from the OP's, this is obviously silly. All this extra panic code was made reachable from compile time evaluation, but we stamped out runtime code for it. So I wonder if that's what we should be tracking here; if reachability determines something is reachable but only at compile time. |
Err yes, that was badly formulated on my part. Fixing it in the description.
That fully explains it, I was not aware that we actually started cross-crate inlining for non-marked functions now. This bugreport was created based on the assumption that that does not happen for public library functions.
Thanks! Good to know I discovered something unexpected at least 😄 |
That seems like what this ought to boil down to, yeah |
const
functions in const
items do not seem to be evaluated at compiletime
Interesting, I guess currently this is entirely per-item and does not consider "how the item is used"? Since we distinguish compile-time-MIR and runtime MIR, it would indeed make sense to treat those two rather separately for reachability. Cc @oli-obk |
I started working on this and made some progress, but then was immediately tripped up by the difference between a |
Having recently looked into the collector a bit, I have no idea how this is happening. We're not recursing into the MIR body of a const, so I can't see how the body of Maybe some other pass is adding junk to |
There are these calls in rust/compiler/rustc_passes/src/reachable.rs Lines 197 to 202 in 24f009c
Does anyone know anyone who understands our mono item collector?^^ |
Per my comment above, I believe this is required to handle |
That doesn't sound right. Function pointers are handled by walking the final value of the const, and if we see a function pointer in there we add it to the monomorphization list. (Pointers are special values -- they have provenance -- so we can detect them in the final value of a const pretty easily; this does not require a type-driven traversal.) There's the |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Stop walking the bodies of statics for reachability, and evaluate them instead cc `@saethlin` `@RalfJung` cc rust-lang#119214 This reuses the `DefIdVisitor` from `rustc_privacy`, because they basically try to do the same thing. It can probably be extended to constants, too, but let's tackle that separately, it's likely more involved.
Okay, so if I understand correctly we still don't have an explanation for why these even get added to the monomorphization set. More research is needed...
|
FWIW this doesn't reproduce on latest nightly any more, the |
Turns out reachability is involved, I think. Just very indirectly. The key difference in the log of the good behavior (without
IOW, this function suddenly considers rust/compiler/rustc_monomorphize/src/collector.rs Lines 1293 to 1307 in d3514a0
So So... I guess the question is, is |
Stop walking the bodies of statics for reachability, and evaluate them instead cc `@saethlin` `@RalfJung` cc rust-lang#119214 This reuses the `DefIdVisitor` from `rustc_privacy`, because they basically try to do the same thing. This PR's changes can probably be extended to constants, too, but let's tackle that separately, it's likely more involved.
So, looking at the comments I wrote in #122769... I think what happens is that since Now, we could probably skip this for top-level const items, since we know we can always evaluate them to a value, and then we can just check the final value for any function pointers and only codegen those. (And the collector already has logic to find these function pointers.) But for associated consts, this is a lot harder. I think we'd have to keep track (inside |
Ah! I think this might explain why my attempt to implement const-reachable didn't work. |
Do you still have that branch somewhere? What I imagine would be completely internal to reachable.rs. At least according to the comments there, we encode ctfe MIR for everything anyway, so it's only runtime reachability we have to worry about.
But, surprisingly, a const item can make things runtime reachable.
|
When a runtime function calls a private
|
Yes, though I expect there isn't anything in it worth salvaging: master...saethlin:rust:const-reachability |
Okay so that computed two sets as result from the query. I don't know if that's necessary. Regarding the analysis outlined above, I assume it is pretty rare that a private function is
|
reachable computation: extend explanation of what this does, and why Follow-up to rust-lang#122769. I had the time to think about this some more, in particular in the context of rust-lang#119214, so I felt it was worth extending these comments some more. I also gave up on the context of "externally reachable" as it is not called that way anywhere else in the compiler. Cc `@tmiasko` `@saethlin`
Rollup merge of rust-lang#124904 - RalfJung:reachable, r=tmiasko reachable computation: extend explanation of what this does, and why Follow-up to rust-lang#122769. I had the time to think about this some more, in particular in the context of rust-lang#119214, so I felt it was worth extending these comments some more. I also gave up on the context of "externally reachable" as it is not called that way anywhere else in the compiler. Cc `@tmiasko` `@saethlin`
reachable computation: extend explanation of what this does, and why Follow-up to rust-lang/rust#122769. I had the time to think about this some more, in particular in the context of rust-lang/rust#119214, so I felt it was worth extending these comments some more. I also gave up on the context of "externally reachable" as it is not called that way anywhere else in the compiler. Cc `@tmiasko` `@saethlin`
Don't walk the bodies of free constants for reachability. follow-up to rust-lang#122371 cc rust-lang#119214 This avoids codegening items (e.g. functions) that are only used during const eval, but do not reach their final constant value (e.g. via function pointers). r? `@tmiasko`
On nightly, godbolt doesn't show any assembly any more with the original reproducer, thanks to #122505. However, this variant still generates assembly: pub struct Thing;
impl Thing {
const fn new(panic: bool) -> Self {
if panic {
panic!()
}
Thing
}
const C: Thing = Thing::new(false);
}
#[inline]
pub fn oof() -> Thing {
Thing::C
} |
Code
I tried this code:
I expected to see this happen: Both
check1
andcheck2
should compile to functions that just use a compiletime-evaluated struct valueInstead, this happened:
check1
disapeared from the assembly, and it looks like all the code needed to runtime-evaluate it got added insteadThe weird thing is that the function that regressed is the one that uses a
const
item by name, rather than the one that just calls a const function inside a non-const function body.Version it worked on
It most recently worked on: 1.74.1 (stable as of writing this)
Version with regression
from playground:
The text was updated successfully, but these errors were encountered: