Skip to content

Commit 773101f

Browse files
committed
update queries for deduplication
1 parent 8cb36a0 commit 773101f

File tree

3 files changed

+111
-64
lines changed

3 files changed

+111
-64
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use rustc_errors::ErrorReported;
1010
use rustc_hir as hir;
1111
use rustc_hir::def::DefKind;
1212
use rustc_middle::mir;
13-
use rustc_middle::mir::interpret::ErrorHandled;
13+
use rustc_middle::mir::interpret::{ConstDedupResult, ErrorHandled};
1414
use rustc_middle::mir::pretty::display_allocation;
1515
use rustc_middle::traits::Reveal;
16-
use rustc_middle::ty::layout::LayoutOf;
16+
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
1717
use rustc_middle::ty::print::with_no_trimmed_paths;
1818
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
1919
use rustc_span::source_map::Span;
@@ -28,6 +28,7 @@ pub fn note_on_undefined_behavior_error() -> &'static str {
2828
}
2929

3030
// Returns a pointer to where the result lives
31+
#[instrument(skip(ecx, body), level = "debug")]
3132
fn eval_body_using_ecx<'mir, 'tcx>(
3233
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
3334
cid: GlobalId<'tcx>,
@@ -160,6 +161,7 @@ pub(super) fn op_to_const<'tcx>(
160161
ConstValue::Scalar(Scalar::ZST)
161162
}
162163
};
164+
163165
match immediate {
164166
Ok(ref mplace) => to_const_value(mplace),
165167
// see comment on `let try_as_immediate` above
@@ -212,77 +214,76 @@ fn turn_into_const_value<'tcx>(
212214
op_to_const(&ecx, &mplace.into())
213215
}
214216

217+
#[instrument(skip(tcx), level = "debug")]
215218
pub fn eval_to_const_value_raw_provider<'tcx>(
216219
tcx: TyCtxt<'tcx>,
217220
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
218221
) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
219222
assert!(key.param_env.constness() == hir::Constness::Const);
220-
// see comment in eval_to_allocation_raw_provider for what we're doing here
221-
if key.param_env.reveal() == Reveal::All {
222-
let mut key = key;
223-
key.param_env = key.param_env.with_user_facing();
224-
match tcx.eval_to_const_value_raw(key) {
225-
// try again with reveal all as requested
226-
Err(ErrorHandled::TooGeneric) => {}
227-
// deduplicate calls
228-
other => return other,
229-
}
230-
}
223+
let (param_env, id) = key.into_parts();
224+
let reveal = param_env.reveal();
231225

232226
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
233227
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
234228
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
235-
let ty = key.value.instance.ty(tcx, key.param_env);
229+
let ty = key.value.instance.ty(tcx, param_env);
236230
let substs = match ty.kind() {
237231
ty::FnDef(_, substs) => substs,
238232
_ => bug!("intrinsic with type {:?}", ty),
239233
};
240-
return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
241-
let span = tcx.def_span(def_id);
242-
let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span };
243-
error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic")
244-
});
234+
235+
match eval_nullary_intrinsic(tcx, param_env, def_id, substs) {
236+
Ok(val) => {
237+
// store result for deduplication
238+
let res = ConstDedupResult::new(reveal, val);
239+
tcx.save_const_value_for_dedup(id, res);
240+
241+
return Ok(val);
242+
}
243+
Err(e) => {
244+
let span = tcx.def_span(def_id);
245+
let error = ConstEvalErr { error: e.into_kind(), stacktrace: vec![], span };
246+
247+
return Err(
248+
error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic")
249+
);
250+
}
251+
}
252+
}
253+
254+
let result =
255+
tcx.dedup_eval_alloc_raw(key, None).map(|val| turn_into_const_value(tcx, val, key));
256+
257+
match result {
258+
Ok(val) => {
259+
tcx.save_const_value_for_dedup(id, ConstDedupResult::new(reveal, val));
260+
}
261+
_ => {}
245262
}
246263

247-
tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
264+
result
248265
}
249266

267+
#[instrument(skip(tcx), level = "debug")]
250268
pub fn eval_to_allocation_raw_provider<'tcx>(
251269
tcx: TyCtxt<'tcx>,
252270
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
253271
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
254272
assert!(key.param_env.constness() == hir::Constness::Const);
255-
// Because the constant is computed twice (once per value of `Reveal`), we are at risk of
256-
// reporting the same error twice here. To resolve this, we check whether we can evaluate the
257-
// constant in the more restrictive `Reveal::UserFacing`, which most likely already was
258-
// computed. For a large percentage of constants that will already have succeeded. Only
259-
// associated constants of generic functions will fail due to not enough monomorphization
260-
// information being available.
261-
262-
// In case we fail in the `UserFacing` variant, we just do the real computation.
263-
if key.param_env.reveal() == Reveal::All {
264-
let mut key = key;
265-
key.param_env = key.param_env.with_user_facing();
266-
match tcx.eval_to_allocation_raw(key) {
267-
// try again with reveal all as requested
268-
Err(ErrorHandled::TooGeneric) => {}
269-
// deduplicate calls
270-
other => return other,
271-
}
272-
}
273+
let (param_env, cid) = key.into_parts();
274+
let reveal = param_env.reveal();
275+
let def = cid.instance.def.with_opt_param();
276+
273277
if cfg!(debug_assertions) {
274278
// Make sure we format the instance even if we do not print it.
275279
// This serves as a regression test against an ICE on printing.
276280
// The next two lines concatenated contain some discussion:
277281
// https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
278282
// subject/anon_const_instance_printing/near/135980032
279-
let instance = with_no_trimmed_paths(|| key.value.instance.to_string());
283+
let instance = with_no_trimmed_paths(|| cid.instance.to_string());
280284
trace!("const eval: {:?} ({})", key, instance);
281285
}
282286

283-
let cid = key.value;
284-
let def = cid.instance.def.with_opt_param();
285-
286287
if let Some(def) = def.as_local() {
287288
if tcx.has_typeck_results(def.did) {
288289
if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors {
@@ -316,6 +317,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
316317
let res = ecx.load_mir(cid.instance.def, cid.promoted);
317318
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
318319
Err(error) => {
320+
debug!("error from eval_body_using_ecx: {:?}", error);
321+
if reveal == Reveal::Selection {
322+
match error.kind() {
323+
err_inval!(Layout(LayoutError::Unknown(_)))
324+
| err_inval!(TooGeneric)
325+
| err_inval!(AlreadyReported(_)) => {
326+
// We do want to report these errors
327+
}
328+
_ => {
329+
return Err(ErrorHandled::Silent);
330+
}
331+
}
332+
}
333+
319334
let err = ConstEvalErr::new(&ecx, error, None);
320335
// Some CTFE errors raise just a lint, not a hard error; see
321336
// <https://github.com/rust-lang/rust/issues/71800>.
@@ -328,7 +343,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
328343
// use of broken constant from other crate: always an error
329344
true
330345
};
331-
332346
if is_hard_err {
333347
let msg = if is_static {
334348
Cow::from("could not evaluate static initializer")
@@ -345,7 +359,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
345359
Cow::from("evaluation of constant value failed")
346360
}
347361
};
348-
349362
Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), &msg))
350363
} else {
351364
let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did);
@@ -377,10 +390,26 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
377390
}
378391
};
379392
let alloc_id = mplace.ptr.provenance.unwrap();
393+
380394
if let Err(error) = validation {
381395
// Validation failed, report an error. This is always a hard error.
382396
let err = ConstEvalErr::new(&ecx, error, None);
383-
Err(err.struct_error(
397+
398+
// FIXME Do we also want to keep these silent with Reveal::Selection?
399+
if reveal == Reveal::Selection {
400+
match err.error {
401+
err_inval!(Layout(LayoutError::Unknown(_)))
402+
| err_inval!(TooGeneric)
403+
| err_inval!(AlreadyReported(_)) => {
404+
// We do want to report these errors
405+
}
406+
_ => {
407+
return Err(ErrorHandled::Silent);
408+
}
409+
}
410+
}
411+
412+
let error = Err(err.struct_error(
384413
ecx.tcx,
385414
"it is undefined behavior to use this value",
386415
|mut diag| {
@@ -394,10 +423,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
394423
));
395424
diag.emit();
396425
},
397-
))
426+
));
427+
428+
debug!(?error);
429+
430+
error
398431
} else {
399432
// Convert to raw constant
400-
Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty })
433+
let const_alloc = ConstAlloc { alloc_id, ty: mplace.layout.ty };
434+
let val = ConstDedupResult::new(reveal, const_alloc);
435+
436+
// store result in order to deduplicate later
437+
tcx.save_alloc_for_dedup(cid, val);
438+
439+
Ok(const_alloc)
401440
}
402441
}
403442
}

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use rustc_middle::ty::{
1818
use rustc_mir_dataflow::storage::AlwaysLiveLocals;
1919
use rustc_query_system::ich::StableHashingContext;
2020
use rustc_session::Limit;
21-
use rustc_span::{Pos, Span};
21+
use rustc_span::Pos;
22+
use rustc_span::Span;
2223
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
2324

2425
use super::{
@@ -265,6 +266,13 @@ impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> {
265266
}
266267
}
267268

269+
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
270+
#[inline]
271+
fn data_layout(&self) -> &TargetDataLayout {
272+
&self.tcx.data_layout
273+
}
274+
}
275+
268276
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
269277
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270278
ty::tls::with(|tcx| {
@@ -291,13 +299,6 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
291299
}
292300
}
293301

294-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
295-
#[inline]
296-
fn data_layout(&self) -> &TargetDataLayout {
297-
&self.tcx.data_layout
298-
}
299-
}
300-
301302
impl<'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'mir, 'tcx, M>
302303
where
303304
M: Machine<'mir, 'tcx>,
@@ -926,6 +927,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
926927
Ok(())
927928
}
928929

930+
#[instrument(skip(self), level = "debug")]
929931
pub fn eval_to_allocation(
930932
&self,
931933
gid: GlobalId<'tcx>,
@@ -941,8 +943,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
941943
self.param_env
942944
};
943945
let param_env = param_env.with_const();
944-
let val = self.tcx.eval_to_allocation_raw(param_env.and(gid))?;
945-
self.raw_const_to_mplace(val)
946+
let val = self.tcx.dedup_eval_alloc_raw(param_env.and(gid), None);
947+
debug!(?val);
948+
949+
self.raw_const_to_mplace(val?)
946950
}
947951

948952
#[must_use]

compiler/rustc_middle/src/mir/interpret/queries.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ impl<'tcx> TyCtxt<'tcx> {
5858
}
5959

6060
/// Evaluate a constant.
61+
#[instrument(skip(self), level = "debug")]
6162
pub fn const_eval_global_id(
6263
self,
6364
param_env: ty::ParamEnv<'tcx>,
@@ -68,11 +69,11 @@ impl<'tcx> TyCtxt<'tcx> {
6869
// Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
6970
// improve caching of queries.
7071
let inputs = self.erase_regions(param_env.and(cid));
71-
if let Some(span) = span {
72-
self.at(span).eval_to_const_value_raw(inputs)
73-
} else {
74-
self.eval_to_const_value_raw(inputs)
75-
}
72+
73+
let raw_const = self.dedup_eval_const_value_raw(inputs, span);
74+
debug!(?raw_const);
75+
76+
raw_const
7677
}
7778

7879
/// Evaluate a static's initializer, returning the allocation of the initializer's memory.
@@ -88,14 +89,17 @@ impl<'tcx> TyCtxt<'tcx> {
8889
}
8990

9091
/// Evaluate anything constant-like, returning the allocation of the final memory.
92+
#[instrument(skip(self), level = "debug")]
9193
fn eval_to_allocation(
9294
self,
9395
gid: GlobalId<'tcx>,
9496
param_env: ty::ParamEnv<'tcx>,
9597
) -> Result<&'tcx mir::Allocation, ErrorHandled> {
9698
let param_env = param_env.with_const();
9799
trace!("eval_to_allocation: Need to compute {:?}", gid);
98-
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
99-
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
100+
let raw_const = self.dedup_eval_alloc_raw(param_env.and(gid), None);
101+
debug!(?raw_const);
102+
103+
Ok(self.global_alloc(raw_const?.alloc_id).unwrap_memory())
100104
}
101105
}

0 commit comments

Comments
 (0)