|
3 | 3 | //! Currently, this pass only propagates scalar values.
|
4 | 4 |
|
5 | 5 | use rustc_const_eval::const_eval::CheckAlignment;
|
6 |
| -use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar}; |
| 6 | +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; |
7 | 7 | use rustc_data_structures::fx::FxHashMap;
|
8 | 8 | use rustc_hir::def::DefKind;
|
| 9 | +use rustc_middle::mir::interpret::{ConstValue, Scalar}; |
9 | 10 | use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor};
|
10 | 11 | use rustc_middle::mir::*;
|
11 | 12 | use rustc_middle::ty::layout::TyAndLayout;
|
12 | 13 | use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
13 | 14 | use rustc_mir_dataflow::value_analysis::{
|
14 |
| - Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, |
| 15 | + Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, |
15 | 16 | };
|
16 | 17 | use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
|
17 |
| -use rustc_span::DUMMY_SP; |
| 18 | +use rustc_span::{Span, DUMMY_SP}; |
18 | 19 | use rustc_target::abi::{Align, FieldIdx, VariantIdx};
|
19 | 20 |
|
20 | 21 | use crate::MirPass;
|
@@ -111,6 +112,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
|
111 | 112 | state: &mut State<Self::Value>,
|
112 | 113 | ) {
|
113 | 114 | match rvalue {
|
| 115 | + Rvalue::Use(operand) => { |
| 116 | + state.flood(target.as_ref(), self.map()); |
| 117 | + if let Some(target) = self.map.find(target.as_ref()) { |
| 118 | + self.assign_operand(state, target, operand); |
| 119 | + } |
| 120 | + } |
114 | 121 | Rvalue::Aggregate(kind, operands) => {
|
115 | 122 | // If we assign `target = Enum::Variant#0(operand)`,
|
116 | 123 | // we must make sure that all `target as Variant#i` are `Top`.
|
@@ -138,8 +145,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
|
138 | 145 | variant_target_idx,
|
139 | 146 | TrackElem::Field(FieldIdx::from_usize(field_index)),
|
140 | 147 | ) {
|
141 |
| - let result = self.handle_operand(operand, state); |
142 |
| - state.insert_idx(field, result, self.map()); |
| 148 | + self.assign_operand(state, field, operand); |
143 | 149 | }
|
144 | 150 | }
|
145 | 151 | }
|
@@ -330,6 +336,86 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
330 | 336 | }
|
331 | 337 | }
|
332 | 338 |
|
| 339 | + /// The caller must have flooded `place`. |
| 340 | + fn assign_operand( |
| 341 | + &self, |
| 342 | + state: &mut State<FlatSet<ScalarInt>>, |
| 343 | + place: PlaceIndex, |
| 344 | + operand: &Operand<'tcx>, |
| 345 | + ) { |
| 346 | + match operand { |
| 347 | + Operand::Copy(rhs) | Operand::Move(rhs) => { |
| 348 | + if let Some(rhs) = self.map.find(rhs.as_ref()) { |
| 349 | + state.insert_place_idx(place, rhs, &self.map) |
| 350 | + } |
| 351 | + } |
| 352 | + Operand::Constant(box constant) => { |
| 353 | + if let Ok(constant) = self.ecx.eval_mir_constant(&constant.literal, None, None) { |
| 354 | + self.assign_constant(state, place, constant, &[]); |
| 355 | + } |
| 356 | + } |
| 357 | + } |
| 358 | + } |
| 359 | + |
| 360 | + /// The caller must have flooded `place`. |
| 361 | + /// |
| 362 | + /// Perform: `place = operand.projection`. |
| 363 | + #[instrument(level = "trace", skip(self, state))] |
| 364 | + fn assign_constant( |
| 365 | + &self, |
| 366 | + state: &mut State<FlatSet<ScalarInt>>, |
| 367 | + place: PlaceIndex, |
| 368 | + mut operand: OpTy<'tcx>, |
| 369 | + projection: &[PlaceElem<'tcx>], |
| 370 | + ) -> Option<!> { |
| 371 | + for &(mut proj_elem) in projection { |
| 372 | + if let PlaceElem::Index(index) = proj_elem { |
| 373 | + if let FlatSet::Elem(index) = state.get(index.into(), &self.map) |
| 374 | + && let Ok(offset) = index.try_to_target_usize(self.tcx) |
| 375 | + && let Some(min_length) = offset.checked_add(1) |
| 376 | + { |
| 377 | + proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; |
| 378 | + } else { |
| 379 | + return None; |
| 380 | + } |
| 381 | + } |
| 382 | + operand = self.ecx.project(&operand, proj_elem).ok()?; |
| 383 | + } |
| 384 | + |
| 385 | + self.map.for_each_projection_value( |
| 386 | + place, |
| 387 | + operand, |
| 388 | + &mut |elem, op| match elem { |
| 389 | + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), |
| 390 | + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), |
| 391 | + TrackElem::Discriminant => { |
| 392 | + let variant = self.ecx.read_discriminant(op).ok()?; |
| 393 | + let scalar = self.ecx.discriminant_for_variant(op.layout, variant).ok()?; |
| 394 | + let discr_ty = op.layout.ty.discriminant_ty(self.tcx); |
| 395 | + let layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?; |
| 396 | + Some(ImmTy::from_scalar(scalar, layout).into()) |
| 397 | + } |
| 398 | + TrackElem::DerefLen => { |
| 399 | + let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); |
| 400 | + let len_usize = op.len(&self.ecx).ok()?; |
| 401 | + let layout = |
| 402 | + self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap(); |
| 403 | + Some(ImmTy::from_uint(len_usize, layout).into()) |
| 404 | + } |
| 405 | + }, |
| 406 | + &mut |place, op| { |
| 407 | + if let Ok(imm) = self.ecx.read_immediate_raw(op) |
| 408 | + && let Some(imm) = imm.right() |
| 409 | + && let Immediate::Scalar(Scalar::Int(scalar)) = *imm |
| 410 | + { |
| 411 | + state.insert_value_idx(place, FlatSet::Elem(scalar), &self.map); |
| 412 | + } |
| 413 | + }, |
| 414 | + ); |
| 415 | + |
| 416 | + None |
| 417 | + } |
| 418 | + |
333 | 419 | fn binary_op(
|
334 | 420 | &self,
|
335 | 421 | state: &mut State<FlatSet<ScalarInt>>,
|
@@ -604,8 +690,16 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
|
604 | 690 | type MemoryKind = !;
|
605 | 691 | const PANIC_ON_ALLOC_FAIL: bool = true;
|
606 | 692 |
|
| 693 | + #[inline(always)] |
| 694 | + fn cur_span(_ecx: &InterpCx<'mir, 'tcx, Self>) -> Span { |
| 695 | + DUMMY_SP |
| 696 | + } |
| 697 | + |
| 698 | + #[inline(always)] |
607 | 699 | fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
|
608 |
| - unimplemented!() |
| 700 | + // We do not check for alignment to avoid having to carry an `Align` |
| 701 | + // in `ConstValue::ByRef`. |
| 702 | + CheckAlignment::No |
609 | 703 | }
|
610 | 704 |
|
611 | 705 | fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
|
|
0 commit comments