-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Reduce the amount of interning and layout_of
calls in const eval.
#74202
Conversation
@bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit 60d032b3f389e3f34d175d0e71658b5ec5e3c85f with merge 5585d82a144a6885bbd224d4aa643c0c95d39fb6... |
cc @rust-lang/wg-const-eval |
☀️ Try build successful - checks-actions, checks-azure |
Queued 5585d82a144a6885bbd224d4aa643c0c95d39fb6 with parent 5db778a, future comparison URL. |
Finished benchmarking try commit (5585d82a144a6885bbd224d4aa643c0c95d39fb6): comparison url. |
booya 5% perf improvement r? @RalfJung |
@bors rollup=never |
src/librustc_middle/ty/sty.rs
Outdated
Err(ErrorHandled::Reported(ErrorReported)) => tcx.const_error(self.ty), | ||
Ok(val) => Some(Ok(val)), | ||
Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, | ||
Err(ErrorHandled::Reported(e)) => Some(Err(e)), |
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.
This looks pretty different from the old code, is all I can say here.^^ No idea if it changes behavior.
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.
this is not the same function. The same function invokes this function adn handles all the cases just like they are handled here.
I have not seen most of this code ever before, so I do not think I can meaningfully review this. I don't even understand how this reduces interning or Wow, we have |
Interning is reduced by evaluating |
Which entry points, concretely, do you mean here? I tried to follow the code in the diff a bit but it is rather hard. There's a dozen functions on 2-3 different types that are all called the same, and when looking at a |
src/librustc_middle/ty/sty.rs
Outdated
} | ||
|
||
#[inline] | ||
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> { | ||
self.try_eval_bits(tcx, param_env, tcx.types.usize).map(|v| v as u64) | ||
self.val.eval(tcx, param_env).try_to_machine_usize(tcx) |
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.
reduces interning
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.
This also reduces layout_of
invocations, because instead of invoking that on tcx.types.usize
, we just use the size in try_to_machine_usize
src/librustc_middle/ty/sty.rs
Outdated
|
||
impl<'tcx> Const<'tcx> { | ||
#[inline] | ||
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> { |
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.
This is used in a mir optimization on every single Assert
terminator:
} if (c.literal.try_eval_bool(tcx, param_env) == Some(true)) == expected => { |
src/librustc_middle/ty/sty.rs
Outdated
1 => Some(true), | ||
_ => None, | ||
}) | ||
self.val.eval(tcx, param_env).try_to_bool() | ||
} | ||
|
||
#[inline] | ||
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> { |
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.
This is used all over the place for array lengths
src/librustc_middle/ty/sty.rs
Outdated
@@ -2333,14 +2333,41 @@ impl<'tcx> Const<'tcx> { | |||
assert_eq!(self.ty, ty); | |||
let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; | |||
// if `ty` does not depend on generic parameters, use an empty param_env | |||
self.eval(tcx, param_env).val.try_to_bits(size) | |||
self.val.eval(tcx, param_env).try_to_bits(size) | |||
} | |||
|
|||
#[inline] | |||
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the | |||
/// unevaluated constant. | |||
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> { |
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.
Oh so on Const
we have try_eval_bits
and eval
but they do not call each other, and there is neither eval_bits
nor try_eval
. But try_eval
exists on ConstKind
. But it is not used to implement Const::try_eval_bits
, at least not directly... I feel I need to draw a call graph by hand to understand what happens.
/// Represents a constant in Rust. | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] | ||
#[derive(HashStable)] | ||
pub enum ConstKind<'tcx> { |
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.
Longer-term I feel like this is really the thing that ought to be called Const
, and the existing Const
should become ConstAndTy
or so... but that is for a separate PR.
☔ The latest upstream changes (presumably #74113) made this pull request unmergeable. Please resolve the merge conflicts. |
r=me after rebasing. |
@bors r=RalfJung |
📌 Commit 1bf0993 has been approved by |
☀️ Test successful - checks-actions, checks-azure |
The perf results from landing don't quite match the earlier results. It's still a win for @oli-obk: did anything change between the earlier run and landing that might explain these worse results? |
The only thing could be some changes around inlining due to the restructuring of the modules, but I don't think this should have an effect within a single crate. If I click the red percentages to look at the details, these regressions aren't actually reflected in the detailed view (or I don't know how to read the detailed view). |
|
While there's indeed a 1.6% regression in |
Ah, no I see now. Incremental loading and execution are separated. And apparently loading |
|
r? @ghost
If we just want to get at some bits of a constant, we don't need to intern it before extracting those bits.
Also, if we want to read a
usize
orbool
, we can fetch the size without invoking a query.