From 6ad6b4381cb38c7562649deb24374889f1f1b9ae Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 13 May 2023 12:30:40 +0000 Subject: [PATCH 01/10] Support non-scalar constants. --- .../src/interpret/eval_context.rs | 4 +- .../rustc_const_eval/src/interpret/machine.rs | 10 ++ .../rustc_mir_dataflow/src/value_analysis.rs | 27 ++++- .../src/dataflow_const_prop.rs | 106 +++++++++++++++++- .../enum.constant.DataflowConstProp.diff | 63 +++++++++++ tests/mir-opt/dataflow-const-prop/enum.rs | 8 ++ ...n.DataflowConstProp.32bit.panic-abort.diff | 7 +- ....DataflowConstProp.32bit.panic-unwind.diff | 7 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 7 +- ....DataflowConstProp.64bit.panic-unwind.diff | 7 +- .../struct.main.DataflowConstProp.diff | 30 +++++ tests/mir-opt/dataflow-const-prop/struct.rs | 5 + 12 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index db1eaf5862179..3bbafada8f6de 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -445,9 +445,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn cur_span(&self) -> Span { - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - self.stack().last().map_or(self.tcx.span, |f| f.current_span()) + M::cur_span(self) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 9fda6b037c8ec..82aca3d30a2ad 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -11,6 +11,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; +use rustc_span::Span; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; @@ -440,6 +441,15 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { frame: Frame<'mir, 'tcx, Self::Provenance>, ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; + fn cur_span(ecx: &InterpCx<'mir, 'tcx, Self>) -> Span + where + 'tcx: 'mir, + { + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + Self::stack(ecx).last().map_or(ecx.tcx.span, |f| f.current_span()) + } + /// Borrow the current thread's stack. fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 299bf692307ab..514146b503013 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -532,7 +532,7 @@ impl State { /// places that are non-overlapping or identical. /// /// The target place must have been flooded before calling this method. - fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) { + pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) { let StateData::Reachable(values) = &mut self.0 else { return }; // If both places are tracked, we copy the value to the target. @@ -928,6 +928,31 @@ impl Map { f(v) } } + + /// Invoke a function on each value in the given place and all descendants. + pub fn for_each_projection_value( + &self, + root: PlaceIndex, + value: O, + project: &mut impl FnMut(TrackElem, &O) -> Option, + f: &mut impl FnMut(PlaceIndex, &O), + ) { + // Fast path is there is nothing to do. + if self.inner_values[root].is_empty() { + return; + } + + if self.places[root].value_index.is_some() { + f(root, &value) + } + + for child in self.children(root) { + let elem = self.places[child].proj_elem.unwrap(); + if let Some(value) = project(elem, &value) { + self.for_each_projection_value(child, value, project, f); + } + } + } } /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`]. diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 333a14be996b0..403d658bccd96 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -3,18 +3,19 @@ //! Currently, this pass only propagates scalar values. use rustc_const_eval::const_eval::CheckAlignment; -use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; +use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{ - Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, + Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; -use rustc_span::DUMMY_SP; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Align, FieldIdx, VariantIdx}; use crate::MirPass; @@ -111,6 +112,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { state: &mut State, ) { match rvalue { + Rvalue::Use(operand) => { + state.flood(target.as_ref(), self.map()); + if let Some(target) = self.map.find(target.as_ref()) { + self.assign_operand(state, target, operand); + } + } Rvalue::Aggregate(kind, operands) => { // If we assign `target = Enum::Variant#0(operand)`, // we must make sure that all `target as Variant#i` are `Top`. @@ -138,8 +145,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { variant_target_idx, TrackElem::Field(FieldIdx::from_usize(field_index)), ) { - let result = self.handle_operand(operand, state); - state.insert_idx(field, result, self.map()); + self.assign_operand(state, field, operand); } } } @@ -330,6 +336,86 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } + /// The caller must have flooded `place`. + fn assign_operand( + &self, + state: &mut State>, + place: PlaceIndex, + operand: &Operand<'tcx>, + ) { + match operand { + Operand::Copy(rhs) | Operand::Move(rhs) => { + if let Some(rhs) = self.map.find(rhs.as_ref()) { + state.insert_place_idx(place, rhs, &self.map) + } + } + Operand::Constant(box constant) => { + if let Ok(constant) = self.ecx.eval_mir_constant(&constant.literal, None, None) { + self.assign_constant(state, place, constant, &[]); + } + } + } + } + + /// The caller must have flooded `place`. + /// + /// Perform: `place = operand.projection`. + #[instrument(level = "trace", skip(self, state))] + fn assign_constant( + &self, + state: &mut State>, + place: PlaceIndex, + mut operand: OpTy<'tcx>, + projection: &[PlaceElem<'tcx>], + ) -> Option { + for &(mut proj_elem) in projection { + if let PlaceElem::Index(index) = proj_elem { + if let FlatSet::Elem(index) = state.get(index.into(), &self.map) + && let Ok(offset) = index.try_to_target_usize(self.tcx) + && let Some(min_length) = offset.checked_add(1) + { + proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; + } else { + return None; + } + } + operand = self.ecx.project(&operand, proj_elem).ok()?; + } + + self.map.for_each_projection_value( + place, + operand, + &mut |elem, op| match elem { + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Discriminant => { + let variant = self.ecx.read_discriminant(op).ok()?; + let scalar = self.ecx.discriminant_for_variant(op.layout, variant).ok()?; + let discr_ty = op.layout.ty.discriminant_ty(self.tcx); + let layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?; + Some(ImmTy::from_scalar(scalar, layout).into()) + } + TrackElem::DerefLen => { + let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); + let len_usize = op.len(&self.ecx).ok()?; + let layout = + self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap(); + Some(ImmTy::from_uint(len_usize, layout).into()) + } + }, + &mut |place, op| { + if let Ok(imm) = self.ecx.read_immediate_raw(op) + && let Some(imm) = imm.right() + && let Immediate::Scalar(Scalar::Int(scalar)) = *imm + { + state.insert_value_idx(place, FlatSet::Elem(scalar), &self.map); + } + }, + ); + + None + } + fn binary_op( &self, state: &mut State>, @@ -604,8 +690,16 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm type MemoryKind = !; const PANIC_ON_ALLOC_FAIL: bool = true; + #[inline(always)] + fn cur_span(_ecx: &InterpCx<'mir, 'tcx, Self>) -> Span { + DUMMY_SP + } + + #[inline(always)] fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment { - unimplemented!() + // We do not check for alignment to avoid having to carry an `Align` + // in `ConstValue::ByRef`. + CheckAlignment::No } fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { diff --git a/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff new file mode 100644 index 0000000000000..07ac5b72e244c --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff @@ -0,0 +1,63 @@ +- // MIR for `constant` before DataflowConstProp ++ // MIR for `constant` after DataflowConstProp + + fn constant() -> () { + let mut _0: (); + let _1: E; + let mut _3: isize; + scope 1 { + debug e => _1; + let _2: i32; + let _4: i32; + let _5: i32; + scope 2 { + debug x => _2; + } + scope 3 { + debug x => _4; + } + scope 4 { + debug x => _5; + } + } + + bb0: { + StorageLive(_1); + _1 = const _; + StorageLive(_2); +- _3 = discriminant(_1); +- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; ++ _3 = const 0_isize; ++ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_5); + _5 = ((_1 as V2).0: i32); + _2 = _5; + StorageDead(_5); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_4); +- _4 = ((_1 as V1).0: i32); +- _2 = _4; ++ _4 = const 0_i32; ++ _2 = const 0_i32; + StorageDead(_4); + goto -> bb4; + } + + bb4: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index 79a20d7ef457d..a70c0abee5b66 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -15,6 +15,13 @@ fn simple() { let x = match e { E::V1(x) => x, E::V2(x) => x }; } +// EMIT_MIR enum.constant.DataflowConstProp.diff +fn constant() { + const C: E = E::V1(0); + let e = C; + let x = match e { E::V1(x) => x, E::V2(x) => x }; +} + #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] struct NonZeroUsize(usize); @@ -63,6 +70,7 @@ fn multiple(x: bool, i: u8) { fn main() { simple(); + constant(); mutate_discriminant(); multiple(false, 5); } diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff index be55d259dcf63..e99b413f708c4 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff @@ -55,11 +55,12 @@ _10 = const _; StorageLive(_11); _11 = const 1_usize; - _12 = Len((*_10)); +- _12 = Len((*_10)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable]; -+ _13 = Lt(const 1_usize, _12); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff index f9a6c509ac8ed..759a793fbf328 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff @@ -55,11 +55,12 @@ _10 = const _; StorageLive(_11); _11 = const 1_usize; - _12 = Len((*_10)); +- _12 = Len((*_10)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue]; -+ _13 = Lt(const 1_usize, _12); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; } bb2: { diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff index be55d259dcf63..e99b413f708c4 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff @@ -55,11 +55,12 @@ _10 = const _; StorageLive(_11); _11 = const 1_usize; - _12 = Len((*_10)); +- _12 = Len((*_10)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable]; -+ _13 = Lt(const 1_usize, _12); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff index f9a6c509ac8ed..759a793fbf328 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff @@ -55,11 +55,12 @@ _10 = const _; StorageLive(_11); _11 = const 1_usize; - _12 = Len((*_10)); +- _12 = Len((*_10)); - _13 = Lt(_11, _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue]; -+ _13 = Lt(const 1_usize, _12); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; } bb2: { diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff index 914bc8ac47e95..d65221158e4ba 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff @@ -7,6 +7,7 @@ let mut _3: i32; let mut _5: i32; let mut _6: i32; + let mut _11: BigStruct; scope 1 { debug s => _1; let _2: i32; @@ -15,6 +16,16 @@ let _4: i32; scope 3 { debug b => _4; + let _7: S; + let _8: u8; + let _9: f32; + let _10: S; + scope 4 { + debug a => _7; + debug b => _8; + debug c => _9; + debug d => _10; + } } } } @@ -41,7 +52,26 @@ + _4 = const 6_i32; StorageDead(_6); StorageDead(_5); + StorageLive(_11); + _11 = const _; + StorageLive(_7); +- _7 = move (_11.0: S); ++ _7 = const S(1_i32); + StorageLive(_8); +- _8 = (_11.1: u8); ++ _8 = const 5_u8; + StorageLive(_9); +- _9 = (_11.2: f32); ++ _9 = const 7f32; + StorageLive(_10); +- _10 = move (_11.3: S); ++ _10 = const S(13_i32); + StorageDead(_11); _0 = const (); + StorageDead(_10); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); StorageDead(_4); StorageDead(_2); StorageDead(_1); diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs index 841b279e03eee..d2cd697db2503 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.rs +++ b/tests/mir-opt/dataflow-const-prop/struct.rs @@ -2,10 +2,15 @@ struct S(i32); +struct BigStruct(S, u8, f32, S); + // EMIT_MIR struct.main.DataflowConstProp.diff fn main() { let mut s = S(1); let a = s.0 + 2; s.0 = 3; let b = a + s.0; + + const VAL: BigStruct = BigStruct(S(1), 5, 7., S(13)); + let BigStruct(a, b, c, d) = VAL; } From 82f0468009da17c14a3ab69d01526db848cf8b55 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 13 May 2023 14:19:31 +0000 Subject: [PATCH 02/10] Handle reading statics. --- .../src/dataflow_const_prop.rs | 124 +++++++++++------ tests/mir-opt/dataflow-const-prop/enum.rs | 13 ++ .../enum.statics.DataflowConstProp.diff | 126 ++++++++++++++++++ .../struct.main.DataflowConstProp.diff | 48 ++++++- tests/mir-opt/dataflow-const-prop/struct.rs | 5 + 5 files changed, 276 insertions(+), 40 deletions(-) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 403d658bccd96..5ec9d13f7d206 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -6,7 +6,7 @@ use rustc_const_eval::const_eval::CheckAlignment; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ConstValue, InterpResult, Scalar}; use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::TyAndLayout; @@ -15,6 +15,7 @@ use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; +use rustc_span::def_id::DefId; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Align, FieldIdx, VariantIdx}; @@ -78,7 +79,7 @@ struct ConstAnalysis<'a, 'tcx> { } impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { - type Value = FlatSet; + type Value = FlatSet; const NAME: &'static str = "ConstAnalysis"; @@ -182,7 +183,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { if let Some(overflow_target) = overflow_target { let overflow = match overflow { FlatSet::Top => FlatSet::Top, - FlatSet::Elem(overflow) => FlatSet::Elem(overflow.into()), + FlatSet::Elem(overflow) => FlatSet::Elem(Scalar::from_bool(overflow)), FlatSet::Bottom => FlatSet::Bottom, }; // We have flooded `target` earlier. @@ -204,7 +205,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { && let ty::Array(_, len) = operand_ty.ty.kind() && let Some(len) = ConstantKind::Ty(*len).eval(self.tcx, self.param_env).try_to_scalar_int() { - state.insert_value_idx(target_len, FlatSet::Elem(len), self.map()); + state.insert_value_idx(target_len, FlatSet::Elem(len.into()), self.map()); } } _ => self.super_assign(target, rvalue, state), @@ -222,7 +223,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { if let ty::Array(_, len) = place_ty.ty.kind() { ConstantKind::Ty(*len) .eval(self.tcx, self.param_env) - .try_to_scalar_int() + .try_to_scalar() .map_or(FlatSet::Top, FlatSet::Elem) } else if let [ProjectionElem::Deref] = place.projection[..] { state.get_len(place.local.into(), self.map()) @@ -281,7 +282,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { .bytes(), _ => return ValueOrPlace::Value(FlatSet::Top), }; - ScalarInt::try_from_target_usize(val, self.tcx).map_or(FlatSet::Top, FlatSet::Elem) + FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx)) } Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), self.map()), _ => return self.super_rvalue(rvalue, state), @@ -297,7 +298,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { constant .literal .eval(self.tcx, self.param_env) - .try_to_scalar_int() + .try_to_scalar() .map_or(FlatSet::Top, FlatSet::Elem) } @@ -339,14 +340,21 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { /// The caller must have flooded `place`. fn assign_operand( &self, - state: &mut State>, + state: &mut State>, place: PlaceIndex, operand: &Operand<'tcx>, ) { match operand { Operand::Copy(rhs) | Operand::Move(rhs) => { if let Some(rhs) = self.map.find(rhs.as_ref()) { - state.insert_place_idx(place, rhs, &self.map) + state.insert_place_idx(place, rhs, &self.map); + } else if rhs.projection.first() == Some(&PlaceElem::Deref) + && let FlatSet::Elem(pointer) = state.get(rhs.local.into(), &self.map) + && let rhs_ty = self.local_decls[rhs.local].ty + && let Ok(rhs_layout) = self.tcx.layout_of(self.param_env.and(rhs_ty)) + { + let op = ImmTy::from_scalar(pointer, rhs_layout).into(); + self.assign_constant(state, place, op, &rhs.projection); } } Operand::Constant(box constant) => { @@ -363,7 +371,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { #[instrument(level = "trace", skip(self, state))] fn assign_constant( &self, - state: &mut State>, + state: &mut State>, place: PlaceIndex, mut operand: OpTy<'tcx>, projection: &[PlaceElem<'tcx>], @@ -371,7 +379,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { for &(mut proj_elem) in projection { if let PlaceElem::Index(index) = proj_elem { if let FlatSet::Elem(index) = state.get(index.into(), &self.map) - && let Ok(offset) = index.try_to_target_usize(self.tcx) + && let Ok(offset) = index.to_target_usize(&self.tcx) && let Some(min_length) = offset.checked_add(1) { proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false }; @@ -406,7 +414,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { &mut |place, op| { if let Ok(imm) = self.ecx.read_immediate_raw(op) && let Some(imm) = imm.right() - && let Immediate::Scalar(Scalar::Int(scalar)) = *imm + && let Immediate::Scalar(scalar) = *imm { state.insert_value_idx(place, FlatSet::Elem(scalar), &self.map); } @@ -418,11 +426,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { fn binary_op( &self, - state: &mut State>, + state: &mut State>, op: BinOp, left: &Operand<'tcx>, right: &Operand<'tcx>, - ) -> (FlatSet, FlatSet) { + ) -> (FlatSet, FlatSet) { let left = self.eval_operand(left, state); let right = self.eval_operand(right, state); @@ -431,9 +439,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { match self.ecx.overflowing_binary_op(op, &left, &right) { - Ok((Scalar::Int(val), overflow, _)) => { - (FlatSet::Elem(val), FlatSet::Elem(overflow)) - } + Ok((val, overflow, _)) => (FlatSet::Elem(val), FlatSet::Elem(overflow)), _ => (FlatSet::Top, FlatSet::Top), } } @@ -445,9 +451,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } let arg_scalar = const_arg.to_scalar(); - let Ok(arg_scalar) = arg_scalar.try_to_int() else { - return (FlatSet::Top, FlatSet::Top); - }; let Ok(arg_value) = arg_scalar.to_bits(layout.size) else { return (FlatSet::Top, FlatSet::Top); }; @@ -473,7 +476,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { fn eval_operand( &self, op: &Operand<'tcx>, - state: &mut State>, + state: &mut State>, ) -> FlatSet> { let value = match self.handle_operand(op, state) { ValueOrPlace::Value(value) => value, @@ -492,24 +495,24 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } - fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option { + fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option { if !enum_ty.is_enum() { return None; } let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?; let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?; - let discr_value = ScalarInt::try_from_uint(discr.val, discr_layout.size)?; + let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?; Some(discr_value) } - fn wrap_immediate(&self, imm: Immediate) -> FlatSet { + fn wrap_immediate(&self, imm: Immediate) -> FlatSet { match imm { - Immediate::Scalar(Scalar::Int(scalar)) => FlatSet::Elem(scalar), + Immediate::Scalar(scalar) => FlatSet::Elem(scalar), _ => FlatSet::Top, } } - fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet { + fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet { self.wrap_immediate(*val) } } @@ -550,7 +553,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper>>> for CollectAndPatch<'tcx, '_> { - type FlowState = State>; + type FlowState = State>; fn visit_statement_before_primary_effect( &mut self, @@ -580,14 +583,10 @@ impl<'mir, 'tcx> // Don't overwrite the assignment if it already uses a constant (to keep the span). } StatementKind::Assign(box (place, _)) => { - match state.get(place.as_ref(), &results.analysis.0.map) { - FlatSet::Top => (), - FlatSet::Elem(value) => { - self.assignments.insert(location, value); - } - FlatSet::Bottom => { - // This assignment is either unreachable, or an uninitialized value is assigned. - } + if let FlatSet::Elem(Scalar::Int(value)) = + state.get(place.as_ref(), &results.analysis.0.map) + { + self.assignments.insert(location, value); } } _ => (), @@ -657,7 +656,7 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx, '_> { } struct OperandCollector<'tcx, 'map, 'locals, 'a> { - state: &'a State>, + state: &'a State>, visitor: &'a mut CollectAndPatch<'tcx, 'locals>, map: &'map Map, } @@ -665,7 +664,7 @@ struct OperandCollector<'tcx, 'map, 'locals, 'a> { impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { if let Some(place) = operand.place() { - if let FlatSet::Elem(value) = self.state.get(place.as_ref(), self.map) { + if let FlatSet::Elem(Scalar::Int(value)) = self.state.get(place.as_ref(), self.map) { self.visitor.before_effect.insert((location, place), value); } else if !place.projection.is_empty() { // Try to propagate into `Index` projections. @@ -676,7 +675,7 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { fn visit_local(&mut self, local: Local, ctxt: PlaceContext, location: Location) { if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy | NonMutatingUseContext::Move) = ctxt - && let FlatSet::Elem(value) = self.state.get(local.into(), self.map) + && let FlatSet::Elem(Scalar::Int(value)) = self.state.get(local.into(), self.map) { self.visitor.before_effect.insert((location, local.into()), value); } @@ -685,6 +684,34 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { struct DummyMachine; +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +macro_rules! throw_machine_stop_str { + ($($tt:tt)*) => {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { + self.to_string().into() + } + + fn add_args( + self: Box, + _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>), + ) {} + } + throw_machine_stop!(Zst) + }}; +} + impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); type MemoryKind = !; @@ -714,6 +741,27 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm unimplemented!() } + fn before_access_global( + _tcx: TyCtxt<'tcx>, + _machine: &Self, + _alloc_id: AllocId, + alloc: ConstAllocation<'tcx>, + _static_def_id: Option, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + throw_machine_stop_str!("can't write to global"); + } + + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if alloc.inner().mutability.is_mut() { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); + } + + Ok(()) + } + fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index a70c0abee5b66..6b7457075922a 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -4,6 +4,7 @@ use std::intrinsics::mir::*; +#[derive(Copy, Clone)] enum E { V1(i32), V2(i32) @@ -22,6 +23,17 @@ fn constant() { let x = match e { E::V1(x) => x, E::V2(x) => x }; } +// EMIT_MIR enum.statics.DataflowConstProp.diff +fn statics() { + static C: E = E::V1(0); + let e = C; + let x = match e { E::V1(x) => x, E::V2(x) => x }; + + static RC: &E = &E::V2(4); + let e = RC; + let x = match e { E::V1(x) => x, E::V2(x) => x }; +} + #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] struct NonZeroUsize(usize); @@ -71,6 +83,7 @@ fn multiple(x: bool, i: u8) { fn main() { simple(); constant(); + statics(); mutate_discriminant(); multiple(false, 5); } diff --git a/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff new file mode 100644 index 0000000000000..63799b3bac323 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff @@ -0,0 +1,126 @@ +- // MIR for `statics` before DataflowConstProp ++ // MIR for `statics` after DataflowConstProp + + fn statics() -> () { + let mut _0: (); + let _1: E; + let mut _2: &E; + let mut _4: isize; + let mut _8: &&E; + let mut _10: isize; + scope 1 { + debug e => _1; + let _3: i32; + let _5: i32; + let _6: i32; + scope 2 { + debug x => _3; + let _7: &E; + scope 5 { + debug e => _7; + let _9: &i32; + let _11: &i32; + let _12: &i32; + scope 6 { + debug x => _9; + } + scope 7 { + debug x => _11; + } + scope 8 { + debug x => _12; + } + } + } + scope 3 { + debug x => _5; + } + scope 4 { + debug x => _6; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = const {alloc1: &E}; + _1 = (*_2); + StorageDead(_2); + StorageLive(_3); +- _4 = discriminant(_1); +- switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2]; ++ _4 = const 0_isize; ++ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_6); + _6 = ((_1 as V2).0: i32); + _3 = _6; + StorageDead(_6); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_5); +- _5 = ((_1 as V1).0: i32); +- _3 = _5; ++ _5 = const 0_i32; ++ _3 = const 0_i32; + StorageDead(_5); + goto -> bb4; + } + + bb4: { + StorageLive(_7); + StorageLive(_8); + _8 = const {alloc2: &&E}; + _7 = (*_8); + StorageDead(_8); + StorageLive(_9); + _10 = discriminant((*_7)); + switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2]; + } + + bb5: { + StorageLive(_12); + _12 = &(((*_7) as V2).0: i32); + _9 = &(*_12); + StorageDead(_12); + goto -> bb7; + } + + bb6: { + StorageLive(_11); + _11 = &(((*_7) as V1).0: i32); + _9 = _11; + StorageDead(_11); + goto -> bb7; + } + + bb7: { + _0 = const (); + StorageDead(_9); + StorageDead(_7); + StorageDead(_3); + StorageDead(_1); + return; + } + } + + alloc2 (static: RC, size: 8, align: 8) { + ╾───────alloc14───────╼ │ ╾──────╼ + } + + alloc14 (size: 8, align: 4) { + 01 00 00 00 04 00 00 00 │ ........ + } + + alloc1 (static: statics::C, size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff index d65221158e4ba..89e37cc04dc23 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff @@ -8,6 +8,12 @@ let mut _5: i32; let mut _6: i32; let mut _11: BigStruct; + let mut _16: &&BigStruct; + let mut _17: &BigStruct; + let mut _18: &BigStruct; + let mut _19: &BigStruct; + let mut _20: &BigStruct; + let mut _21: &BigStruct; scope 1 { debug s => _1; let _2: i32; @@ -25,6 +31,16 @@ debug b => _8; debug c => _9; debug d => _10; + let _12: S; + let _13: u8; + let _14: f32; + let _15: S; + scope 5 { + debug a => _12; + debug b => _13; + debug c => _14; + debug d => _15; + } } } } @@ -55,7 +71,7 @@ StorageLive(_11); _11 = const _; StorageLive(_7); -- _7 = move (_11.0: S); +- _7 = (_11.0: S); + _7 = const S(1_i32); StorageLive(_8); - _8 = (_11.1: u8); @@ -64,10 +80,30 @@ - _9 = (_11.2: f32); + _9 = const 7f32; StorageLive(_10); -- _10 = move (_11.3: S); +- _10 = (_11.3: S); + _10 = const S(13_i32); StorageDead(_11); + StorageLive(_16); + _16 = const {alloc1: &&BigStruct}; + _17 = deref_copy (*_16); + StorageLive(_12); + _18 = deref_copy (*_16); + _12 = ((*_18).0: S); + StorageLive(_13); + _19 = deref_copy (*_16); + _13 = ((*_19).1: u8); + StorageLive(_14); + _20 = deref_copy (*_16); + _14 = ((*_20).2: f32); + StorageLive(_15); + _21 = deref_copy (*_16); + _15 = ((*_21).3: S); + StorageDead(_16); _0 = const (); + StorageDead(_15); + StorageDead(_14); + StorageDead(_13); + StorageDead(_12); StorageDead(_10); StorageDead(_9); StorageDead(_8); @@ -79,3 +115,11 @@ } } + alloc1 (static: STAT, size: 8, align: 8) { + ╾───────alloc15───────╼ │ ╾──────╼ + } + + alloc15 (size: 16, align: 4) { + 01 00 00 00 00 00 e0 40 0d 00 00 00 05 __ __ __ │ .......@.....░░░ + } + diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs index d2cd697db2503..c171ed74c5439 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.rs +++ b/tests/mir-opt/dataflow-const-prop/struct.rs @@ -1,7 +1,9 @@ // unit-test: DataflowConstProp +#[derive(Copy, Clone)] struct S(i32); +#[derive(Copy, Clone)] struct BigStruct(S, u8, f32, S); // EMIT_MIR struct.main.DataflowConstProp.diff @@ -13,4 +15,7 @@ fn main() { const VAL: BigStruct = BigStruct(S(1), 5, 7., S(13)); let BigStruct(a, b, c, d) = VAL; + + static STAT: &BigStruct = &BigStruct(S(1), 5, 7., S(13)); + let BigStruct(a, b, c, d) = *STAT; } From b851e554ddf64d4889d305ee4d8972028bc86b90 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 7 Sep 2023 17:13:58 +0000 Subject: [PATCH 03/10] Support CopyForDeref. --- .../rustc_mir_transform/src/dataflow_const_prop.rs | 6 ++++++ .../struct.main.DataflowConstProp.diff | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 5ec9d13f7d206..fc8eccb270516 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -119,6 +119,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { self.assign_operand(state, target, operand); } } + Rvalue::CopyForDeref(rhs) => { + state.flood(target.as_ref(), self.map()); + if let Some(target) = self.map.find(target.as_ref()) { + self.assign_operand(state, target, &Operand::Copy(*rhs)); + } + } Rvalue::Aggregate(kind, operands) => { // If we assign `target = Enum::Variant#0(operand)`, // we must make sure that all `target as Variant#i` are `Top`. diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff index 89e37cc04dc23..71a28f2165b26 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff @@ -88,16 +88,20 @@ _17 = deref_copy (*_16); StorageLive(_12); _18 = deref_copy (*_16); - _12 = ((*_18).0: S); +- _12 = ((*_18).0: S); ++ _12 = const S(1_i32); StorageLive(_13); _19 = deref_copy (*_16); - _13 = ((*_19).1: u8); +- _13 = ((*_19).1: u8); ++ _13 = const 5_u8; StorageLive(_14); _20 = deref_copy (*_16); - _14 = ((*_20).2: f32); +- _14 = ((*_20).2: f32); ++ _14 = const 7f32; StorageLive(_15); _21 = deref_copy (*_16); - _15 = ((*_21).3: S); +- _15 = ((*_21).3: S); ++ _15 = const S(13_i32); StorageDead(_16); _0 = const (); StorageDead(_15); From 1d6a32c920d03f34facf6af3b3aea75ce8c48d4b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 7 Sep 2023 17:20:51 +0000 Subject: [PATCH 04/10] Interpret Immediate::Uninit as Bottom. --- .../src/dataflow_const_prop.rs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index fc8eccb270516..a67bc53d2b4bb 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -270,9 +270,10 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { val } Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) { - FlatSet::Elem(value) => { - self.ecx.unary_op(*op, &value).map_or(FlatSet::Top, |val| self.wrap_immty(val)) - } + FlatSet::Elem(value) => self + .ecx + .unary_op(*op, &value) + .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, }, @@ -420,9 +421,9 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { &mut |place, op| { if let Ok(imm) = self.ecx.read_immediate_raw(op) && let Some(imm) = imm.right() - && let Immediate::Scalar(scalar) = *imm { - state.insert_value_idx(place, FlatSet::Elem(scalar), &self.map); + let elem = self.wrap_immediate(*imm); + state.insert_value_idx(place, elem, &self.map); } }, ); @@ -492,10 +493,9 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { FlatSet::Top => FlatSet::Top, FlatSet::Elem(scalar) => { let ty = op.ty(self.local_decls, self.tcx); - self.tcx - .layout_of(self.param_env.and(ty)) - .map(|layout| FlatSet::Elem(ImmTy::from_scalar(scalar.into(), layout))) - .unwrap_or(FlatSet::Top) + self.tcx.layout_of(self.param_env.and(ty)).map_or(FlatSet::Top, |layout| { + FlatSet::Elem(ImmTy::from_scalar(scalar.into(), layout)) + }) } FlatSet::Bottom => FlatSet::Bottom, } @@ -514,13 +514,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { fn wrap_immediate(&self, imm: Immediate) -> FlatSet { match imm { Immediate::Scalar(scalar) => FlatSet::Elem(scalar), + Immediate::Uninit => FlatSet::Bottom, _ => FlatSet::Top, } } - - fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet { - self.wrap_immediate(*val) - } } struct CollectAndPatch<'tcx, 'locals> { From d278ce126cb935008374d2668911b4c38eb73387 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 9 Sep 2023 16:35:40 +0000 Subject: [PATCH 05/10] Return ImmTy in discriminant_for_variant. --- .../rustc_const_eval/src/interpret/discriminant.rs | 7 ++++--- .../rustc_const_eval/src/interpret/intrinsics.rs | 2 +- compiler/rustc_const_eval/src/interpret/step.rs | 2 +- .../rustc_mir_transform/src/dataflow_const_prop.rs | 13 +++++-------- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6c35fb01a93b1..440ee06e7fe70 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -247,9 +247,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, layout: TyAndLayout<'tcx>, variant: VariantIdx, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?; - Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) { + let discr_value = match layout.ty.discriminant_for_variant(*self.tcx, variant) { Some(discr) => { // This type actually has discriminants. assert_eq!(discr.ty, discr_layout.ty); @@ -260,6 +260,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(variant.as_u32(), 0); Scalar::from_uint(variant.as_u32(), discr_layout.size) } - }) + }; + Ok(ImmTy::from_scalar(discr_value, discr_layout)) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 7feed74ceae17..e886fd23ff5dd 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -221,7 +221,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::discriminant_value => { let place = self.deref_pointer(&args[0])?; let variant = self.read_discriminant(&place)?; - let discr = self.discriminant_for_variant(place.layout, variant)?; + let discr = self.discriminant_for_variant(place.layout, variant)?.to_scalar(); self.write_scalar(discr, dest)?; } sym::exact_div => { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 57f99bd8f617e..cf1f7ff75e1af 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -301,7 +301,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let op = self.eval_place_to_op(place, None)?; let variant = self.read_discriminant(&op)?; let discr = self.discriminant_for_variant(op.layout, variant)?; - self.write_scalar(discr, &dest)?; + self.write_immediate(*discr, &dest)?; } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index a67bc53d2b4bb..9f3f396a237e1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -405,10 +405,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), TrackElem::Discriminant => { let variant = self.ecx.read_discriminant(op).ok()?; - let scalar = self.ecx.discriminant_for_variant(op.layout, variant).ok()?; - let discr_ty = op.layout.ty.discriminant_ty(self.tcx); - let layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?; - Some(ImmTy::from_scalar(scalar, layout).into()) + let discr_value = self.ecx.discriminant_for_variant(op.layout, variant).ok()?; + Some(discr_value.into()) } TrackElem::DerefLen => { let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); @@ -505,10 +503,9 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { if !enum_ty.is_enum() { return None; } - let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?; - let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?; - let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?; - Some(discr_value) + let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; + let discr_value = self.ecx.discriminant_for_variant(enum_ty_layout, variant_index).ok()?; + Some(discr_value.to_scalar()) } fn wrap_immediate(&self, imm: Immediate) -> FlatSet { From 4901893721c34234ea8886d121a9c281cce0af3a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 9 Sep 2023 16:35:49 +0000 Subject: [PATCH 06/10] Inline callback. --- compiler/rustc_const_eval/src/interpret/machine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 82aca3d30a2ad..0fe99085d6784 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -441,6 +441,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { frame: Frame<'mir, 'tcx, Self::Provenance>, ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; + #[inline(always)] fn cur_span(ecx: &InterpCx<'mir, 'tcx, Self>) -> Span where 'tcx: 'mir, From 24adf07e5f7a6097088787c3658a1b98b772df5d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 9 Sep 2023 16:39:21 +0000 Subject: [PATCH 07/10] Reuse throw_machine_stop_str! macro. --- .../rustc_mir_transform/src/const_prop.rs | 44 +++++++++---------- .../src/dataflow_const_prop.rs | 32 +------------- compiler/rustc_mir_transform/src/lib.rs | 1 + 3 files changed, 24 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 5f135e96980f1..51f2ededed3b3 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -32,32 +32,30 @@ const MAX_ALLOC_LIMIT: u64 = 1024; /// Macro for machine-specific `InterpError` without allocation. /// (These will never be shown to the user, but they help diagnose ICEs.) -macro_rules! throw_machine_stop_str { - ($($tt:tt)*) => {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } +pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) } + } - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { - self.to_string().into() - } - - fn add_args( - self: Box, - _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>), - ) {} + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { + self.to_string().into() } - throw_machine_stop!(Zst) - }}; -} + + fn add_args( + self: Box, + _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>), + ) {} + } + throw_machine_stop!(Zst) +}} pub struct ConstProp; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 9f3f396a237e1..421020d0bd1cf 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -684,34 +684,6 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { struct DummyMachine; -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -macro_rules! throw_machine_stop_str { - ($($tt:tt)*) => {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { - self.to_string().into() - } - - fn add_args( - self: Box, - _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>), - ) {} - } - throw_machine_stop!(Zst) - }}; -} - impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); type MemoryKind = !; @@ -750,13 +722,13 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm is_write: bool, ) -> InterpResult<'tcx> { if is_write { - throw_machine_stop_str!("can't write to global"); + crate::const_prop::throw_machine_stop_str!("can't write to global"); } // If the static allocation is mutable, then we can't const prop it as its content // might be different at runtime. if alloc.inner().mutability.is_mut() { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); + crate::const_prop::throw_machine_stop_str!("can't access mutable globals in ConstProp"); } Ok(()) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index bf798adee199e..ff4385e811697 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -2,6 +2,7 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![feature(box_patterns)] +#![feature(decl_macro)] #![feature(is_sorted)] #![feature(let_chains)] #![feature(map_try_insert)] From bcfe1a4bf2347e2f8df209f72930b335d5edddcf Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 9 Sep 2023 16:41:57 +0000 Subject: [PATCH 08/10] Remove cur_span hook. --- .../rustc_const_eval/src/interpret/eval_context.rs | 4 +++- compiler/rustc_const_eval/src/interpret/machine.rs | 11 ----------- .../rustc_mir_transform/src/dataflow_const_prop.rs | 10 +++------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 3bbafada8f6de..db1eaf5862179 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -445,7 +445,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn cur_span(&self) -> Span { - M::cur_span(self) + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + self.stack().last().map_or(self.tcx.span, |f| f.current_span()) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 0fe99085d6784..9fda6b037c8ec 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -11,7 +11,6 @@ use rustc_middle::mir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_span::Span; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; @@ -441,16 +440,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { frame: Frame<'mir, 'tcx, Self::Provenance>, ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; - #[inline(always)] - fn cur_span(ecx: &InterpCx<'mir, 'tcx, Self>) -> Span - where - 'tcx: 'mir, - { - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - Self::stack(ecx).last().map_or(ecx.tcx.span, |f| f.current_span()) - } - /// Borrow the current thread's stack. fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 421020d0bd1cf..13550672eccc8 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -16,7 +16,7 @@ use rustc_mir_dataflow::value_analysis::{ }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; use rustc_span::def_id::DefId; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::DUMMY_SP; use rustc_target::abi::{Align, FieldIdx, VariantIdx}; use crate::MirPass; @@ -689,11 +689,6 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm type MemoryKind = !; const PANIC_ON_ALLOC_FAIL: bool = true; - #[inline(always)] - fn cur_span(_ecx: &InterpCx<'mir, 'tcx, Self>) -> Span { - DUMMY_SP - } - #[inline(always)] fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment { // We do not check for alignment to avoid having to carry an `Align` @@ -802,7 +797,8 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm _ecx: &'a InterpCx<'mir, 'tcx, Self>, ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { - unimplemented!() + // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. + &[] } fn stack_mut<'a>( From 7493ad0abec1ecbd5f8a9d14a67cc3197e6176c7 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 9 Sep 2023 16:47:17 +0000 Subject: [PATCH 09/10] Duplicate output for test. --- ...num.constant.DataflowConstProp.32bit.diff} | 0 ...enum.constant.DataflowConstProp.64bit.diff | 63 +++++++++ ...num.multiple.DataflowConstProp.32bit.diff} | 0 ...enum.multiple.DataflowConstProp.64bit.diff | 82 +++++++++++ ...discriminant.DataflowConstProp.32bit.diff} | 0 ..._discriminant.DataflowConstProp.64bit.diff | 26 ++++ tests/mir-opt/dataflow-const-prop/enum.rs | 1 + ... enum.simple.DataflowConstProp.32bit.diff} | 0 .../enum.simple.DataflowConstProp.64bit.diff | 63 +++++++++ .../enum.statics.DataflowConstProp.32bit.diff | 126 +++++++++++++++++ ...enum.statics.DataflowConstProp.64bit.diff} | 0 .../struct.main.DataflowConstProp.32bit.diff | 129 ++++++++++++++++++ ... struct.main.DataflowConstProp.64bit.diff} | 0 tests/mir-opt/dataflow-const-prop/struct.rs | 1 + 14 files changed, 491 insertions(+) rename tests/mir-opt/dataflow-const-prop/{enum.constant.DataflowConstProp.diff => enum.constant.DataflowConstProp.32bit.diff} (100%) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff rename tests/mir-opt/dataflow-const-prop/{enum.multiple.DataflowConstProp.diff => enum.multiple.DataflowConstProp.32bit.diff} (100%) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff rename tests/mir-opt/dataflow-const-prop/{enum.mutate_discriminant.DataflowConstProp.diff => enum.mutate_discriminant.DataflowConstProp.32bit.diff} (100%) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff rename tests/mir-opt/dataflow-const-prop/{enum.simple.DataflowConstProp.diff => enum.simple.DataflowConstProp.32bit.diff} (100%) create mode 100644 tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff create mode 100644 tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff rename tests/mir-opt/dataflow-const-prop/{enum.statics.DataflowConstProp.diff => enum.statics.DataflowConstProp.64bit.diff} (100%) create mode 100644 tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff rename tests/mir-opt/dataflow-const-prop/{struct.main.DataflowConstProp.diff => struct.main.DataflowConstProp.64bit.diff} (100%) diff --git a/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.32bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff new file mode 100644 index 0000000000000..07ac5b72e244c --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.constant.DataflowConstProp.64bit.diff @@ -0,0 +1,63 @@ +- // MIR for `constant` before DataflowConstProp ++ // MIR for `constant` after DataflowConstProp + + fn constant() -> () { + let mut _0: (); + let _1: E; + let mut _3: isize; + scope 1 { + debug e => _1; + let _2: i32; + let _4: i32; + let _5: i32; + scope 2 { + debug x => _2; + } + scope 3 { + debug x => _4; + } + scope 4 { + debug x => _5; + } + } + + bb0: { + StorageLive(_1); + _1 = const _; + StorageLive(_2); +- _3 = discriminant(_1); +- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; ++ _3 = const 0_isize; ++ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_5); + _5 = ((_1 as V2).0: i32); + _2 = _5; + StorageDead(_5); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_4); +- _4 = ((_1 as V1).0: i32); +- _2 = _4; ++ _4 = const 0_i32; ++ _2 = const 0_i32; + StorageDead(_4); + goto -> bb4; + } + + bb4: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.32bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.32bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff new file mode 100644 index 0000000000000..775325c4d0626 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.multiple.DataflowConstProp.64bit.diff @@ -0,0 +1,82 @@ +- // MIR for `multiple` before DataflowConstProp ++ // MIR for `multiple` after DataflowConstProp + + fn multiple(_1: bool, _2: u8) -> () { + debug x => _1; + debug i => _2; + let mut _0: (); + let _3: std::option::Option; + let mut _4: bool; + let mut _5: u8; + let mut _7: isize; + scope 1 { + debug e => _3; + let _6: u8; + let _8: u8; + scope 2 { + debug x => _6; + let _9: u8; + scope 4 { + debug y => _9; + } + } + scope 3 { + debug i => _8; + } + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = _1; + switchInt(move _4) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + StorageLive(_5); + _5 = _2; + _3 = Option::::Some(move _5); + StorageDead(_5); + goto -> bb3; + } + + bb2: { + _3 = Option::::None; + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageLive(_6); + _7 = discriminant(_3); + switchInt(move _7) -> [0: bb4, 1: bb6, otherwise: bb5]; + } + + bb4: { + _6 = const 0_u8; + goto -> bb7; + } + + bb5: { + unreachable; + } + + bb6: { + StorageLive(_8); + _8 = ((_3 as Some).0: u8); + _6 = _8; + StorageDead(_8); + goto -> bb7; + } + + bb7: { + StorageLive(_9); + _9 = _6; + _0 = const (); + StorageDead(_9); + StorageDead(_6); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.32bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.32bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff new file mode 100644 index 0000000000000..960e69ee9165e --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.mutate_discriminant.DataflowConstProp.64bit.diff @@ -0,0 +1,26 @@ +- // MIR for `mutate_discriminant` before DataflowConstProp ++ // MIR for `mutate_discriminant` after DataflowConstProp + + fn mutate_discriminant() -> u8 { + let mut _0: u8; + let mut _1: std::option::Option; + let mut _2: isize; + + bb0: { + discriminant(_1) = 1; + (((_1 as variant#1).0: NonZeroUsize).0: usize) = const 0_usize; + _2 = discriminant(_1); + switchInt(_2) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + _0 = const 1_u8; + return; + } + + bb2: { + _0 = const 2_u8; + unreachable; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index 6b7457075922a..5a10e9e883d07 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -1,4 +1,5 @@ // unit-test: DataflowConstProp +// EMIT_MIR_FOR_EACH_BIT_WIDTH #![feature(custom_mir, core_intrinsics, rustc_attrs)] diff --git a/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.32bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.32bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff new file mode 100644 index 0000000000000..3946e7c7d96c1 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.simple.DataflowConstProp.64bit.diff @@ -0,0 +1,63 @@ +- // MIR for `simple` before DataflowConstProp ++ // MIR for `simple` after DataflowConstProp + + fn simple() -> () { + let mut _0: (); + let _1: E; + let mut _3: isize; + scope 1 { + debug e => _1; + let _2: i32; + let _4: i32; + let _5: i32; + scope 2 { + debug x => _2; + } + scope 3 { + debug x => _4; + } + scope 4 { + debug x => _5; + } + } + + bb0: { + StorageLive(_1); + _1 = E::V1(const 0_i32); + StorageLive(_2); +- _3 = discriminant(_1); +- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; ++ _3 = const 0_isize; ++ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_5); + _5 = ((_1 as V2).0: i32); + _2 = _5; + StorageDead(_5); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_4); +- _4 = ((_1 as V1).0: i32); +- _2 = _4; ++ _4 = const 0_i32; ++ _2 = const 0_i32; + StorageDead(_4); + goto -> bb4; + } + + bb4: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff new file mode 100644 index 0000000000000..ae8b44c953e35 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.32bit.diff @@ -0,0 +1,126 @@ +- // MIR for `statics` before DataflowConstProp ++ // MIR for `statics` after DataflowConstProp + + fn statics() -> () { + let mut _0: (); + let _1: E; + let mut _2: &E; + let mut _4: isize; + let mut _8: &&E; + let mut _10: isize; + scope 1 { + debug e => _1; + let _3: i32; + let _5: i32; + let _6: i32; + scope 2 { + debug x => _3; + let _7: &E; + scope 5 { + debug e => _7; + let _9: &i32; + let _11: &i32; + let _12: &i32; + scope 6 { + debug x => _9; + } + scope 7 { + debug x => _11; + } + scope 8 { + debug x => _12; + } + } + } + scope 3 { + debug x => _5; + } + scope 4 { + debug x => _6; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = const {alloc1: &E}; + _1 = (*_2); + StorageDead(_2); + StorageLive(_3); +- _4 = discriminant(_1); +- switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2]; ++ _4 = const 0_isize; ++ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_6); + _6 = ((_1 as V2).0: i32); + _3 = _6; + StorageDead(_6); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + StorageLive(_5); +- _5 = ((_1 as V1).0: i32); +- _3 = _5; ++ _5 = const 0_i32; ++ _3 = const 0_i32; + StorageDead(_5); + goto -> bb4; + } + + bb4: { + StorageLive(_7); + StorageLive(_8); + _8 = const {alloc2: &&E}; + _7 = (*_8); + StorageDead(_8); + StorageLive(_9); + _10 = discriminant((*_7)); + switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2]; + } + + bb5: { + StorageLive(_12); + _12 = &(((*_7) as V2).0: i32); + _9 = &(*_12); + StorageDead(_12); + goto -> bb7; + } + + bb6: { + StorageLive(_11); + _11 = &(((*_7) as V1).0: i32); + _9 = _11; + StorageDead(_11); + goto -> bb7; + } + + bb7: { + _0 = const (); + StorageDead(_9); + StorageDead(_7); + StorageDead(_3); + StorageDead(_1); + return; + } + } + + alloc2 (static: RC, size: 4, align: 4) { + ╾─alloc14─╼ │ ╾──╼ + } + + alloc14 (size: 8, align: 4) { + 01 00 00 00 04 00 00 00 │ ........ + } + + alloc1 (static: statics::C, size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/enum.statics.DataflowConstProp.64bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff new file mode 100644 index 0000000000000..2de6ba307d589 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff @@ -0,0 +1,129 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let mut _1: S; + let mut _3: i32; + let mut _5: i32; + let mut _6: i32; + let mut _11: BigStruct; + let mut _16: &&BigStruct; + let mut _17: &BigStruct; + let mut _18: &BigStruct; + let mut _19: &BigStruct; + let mut _20: &BigStruct; + let mut _21: &BigStruct; + scope 1 { + debug s => _1; + let _2: i32; + scope 2 { + debug a => _2; + let _4: i32; + scope 3 { + debug b => _4; + let _7: S; + let _8: u8; + let _9: f32; + let _10: S; + scope 4 { + debug a => _7; + debug b => _8; + debug c => _9; + debug d => _10; + let _12: S; + let _13: u8; + let _14: f32; + let _15: S; + scope 5 { + debug a => _12; + debug b => _13; + debug c => _14; + debug d => _15; + } + } + } + } + } + + bb0: { + StorageLive(_1); + _1 = S(const 1_i32); + StorageLive(_2); + StorageLive(_3); +- _3 = (_1.0: i32); +- _2 = Add(move _3, const 2_i32); ++ _3 = const 1_i32; ++ _2 = const 3_i32; + StorageDead(_3); + (_1.0: i32) = const 3_i32; + StorageLive(_4); + StorageLive(_5); +- _5 = _2; ++ _5 = const 3_i32; + StorageLive(_6); +- _6 = (_1.0: i32); +- _4 = Add(move _5, move _6); ++ _6 = const 3_i32; ++ _4 = const 6_i32; + StorageDead(_6); + StorageDead(_5); + StorageLive(_11); + _11 = const _; + StorageLive(_7); +- _7 = (_11.0: S); ++ _7 = const S(1_i32); + StorageLive(_8); +- _8 = (_11.1: u8); ++ _8 = const 5_u8; + StorageLive(_9); +- _9 = (_11.2: f32); ++ _9 = const 7f32; + StorageLive(_10); +- _10 = (_11.3: S); ++ _10 = const S(13_i32); + StorageDead(_11); + StorageLive(_16); + _16 = const {alloc1: &&BigStruct}; + _17 = deref_copy (*_16); + StorageLive(_12); + _18 = deref_copy (*_16); +- _12 = ((*_18).0: S); ++ _12 = const S(1_i32); + StorageLive(_13); + _19 = deref_copy (*_16); +- _13 = ((*_19).1: u8); ++ _13 = const 5_u8; + StorageLive(_14); + _20 = deref_copy (*_16); +- _14 = ((*_20).2: f32); ++ _14 = const 7f32; + StorageLive(_15); + _21 = deref_copy (*_16); +- _15 = ((*_21).3: S); ++ _15 = const S(13_i32); + StorageDead(_16); + _0 = const (); + StorageDead(_15); + StorageDead(_14); + StorageDead(_13); + StorageDead(_12); + StorageDead(_10); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + StorageDead(_4); + StorageDead(_2); + StorageDead(_1); + return; + } + } + + alloc1 (static: STAT, size: 4, align: 4) { + ╾─alloc15─╼ │ ╾──╼ + } + + alloc15 (size: 16, align: 4) { + 01 00 00 00 00 00 e0 40 0d 00 00 00 05 __ __ __ │ .......@.....░░░ + } + diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff similarity index 100% rename from tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff rename to tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs index c171ed74c5439..e92a1676d3f53 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.rs +++ b/tests/mir-opt/dataflow-const-prop/struct.rs @@ -1,4 +1,5 @@ // unit-test: DataflowConstProp +// EMIT_MIR_FOR_EACH_BIT_WIDTH #[derive(Copy, Clone)] struct S(i32); From 6984030f80facdda72dd81c209fa19d1e830988e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 11 Sep 2023 16:10:27 +0000 Subject: [PATCH 10/10] Use write_immediate. --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e886fd23ff5dd..3b58f66353b27 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -221,8 +221,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::discriminant_value => { let place = self.deref_pointer(&args[0])?; let variant = self.read_discriminant(&place)?; - let discr = self.discriminant_for_variant(place.layout, variant)?.to_scalar(); - self.write_scalar(discr, dest)?; + let discr = self.discriminant_for_variant(place.layout, variant)?; + self.write_immediate(*discr, dest)?; } sym::exact_div => { let l = self.read_immediate(&args[0])?;