-
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
[EXPERIMENT] Reduction of monomorphization load through type erasure #85308
Conversation
Some changes occured to rustc_codegen_cranelift cc @bjorn3 Some changes occured to the CTFE / Miri engine cc @rust-lang/miri Some changes occurred in src/tools/clippy. cc @rust-lang/clippy |
r? @jackh726 (rust-highfive has picked a reviewer for you, use r? to override) |
The job Click to see the possible cause of the failure (guessed by this bot)
|
@@ -425,6 +433,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |||
// recurse with concrete function | |||
self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind) | |||
} | |||
ty::InstanceDef::ErasedShim(_) => { | |||
bug!("Interpreting type-erased functions is not implemented.") |
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.
If this actually lands, I'd appreciate help making this code work in Miri. :)
Also, if the CTFE engine does not support these calls, we must make sure they are rejected in const fn
.
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.
If this ever lands, these calls will only be created by a MIR-opt pass. As a consequence, they should not appear in a const fn
.
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.
Even optimized code must, IMO, be executable by the Miri engine -- and indeed it is possible to run Miri on optimized MIR with -Zmir-opt-level=4
. The code here is relevant well beyond const fn
. So while it is okay to temporarily land a feature without Miri engine support, this is always a bug that should be fixed eventually.
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.
But I guess this means we indeed don't have to reject anything in const fn
. However, we only recently stopped optimizing the MIR used by CTFE, and we might revert that decision at some point, so there's a danger of some lock-in here.
I think this definitely needs an MCP and someone with more knowledge in this area to review. |
☔ The latest upstream changes (presumably #86993) made this pull request unmergeable. Please resolve the merge conflicts. |
Going to go ahead and close as an inactive "experiment", but of course feel free to reopen if you make further progress or this becomes ready for review -- I agree with @jackh726 that a MCP may be warranted. |
This is an early proposition in response to #77960 through type-erasure of a function's MIR. This is still an experiment, and there are probably a lot of edge cases I did not cover. Any weird addition to the test cases is welcomed 😄
Here is the description I gave in #77960 (comment). Consider a function
During MIR analyses, the trampoline and the callee version can be computed generically:
The next step is to only have one instance of foo_impl. In rustc parlance, I introduced an new variant
InstanceDef::ErasedShim
to encode it. It is resolved when some a MIR call terminator has the new flagerased = true
. In order to define the substitutions for this instance, we replace all the type parameters byu8
. The code becomes:For this transformation to be sound, we need to check that all those transmutes are safe.
The problem is that Rust layouts all monomorphized types independently: in general
M<T>
andM<T_impl>
have no reason to have a similar layout if T != U. In special cases, this can happen, for instance ifM<T>
only points to sized T through a reference.As a consequence, we need to check that
M<T>
andM<T_impl>
and all recursively accessible types have the same layout. Likewise forC<T>
andR<T>
. If any such test fails, the type-erasure is unsound, we need to abort and go back to full monomorphization. Actually, we should be able to restrict the check to all types that are involved in the MIR: types of locals, of accessed fields, of dereferenced pointers...The implementation actually does this in reverse. First, we check the layout in the function
gather_types
, and bail out if there is an incompatibility. Second, we gather and replace all the constants in the MIR. Handling drops requires to convert them into calls todrop_in_place
in the same step. Third, we build the caller MIR. Finally, we erased all the type parameters in the callee MIR.cc @jumbatm @jyn514