Skip to content

Commit 6ad6b43

Browse files
committed
Support non-scalar constants.
1 parent 68c2f5b commit 6ad6b43

12 files changed

+259
-22
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
445445

446446
#[inline(always)]
447447
pub fn cur_span(&self) -> Span {
448-
// This deliberately does *not* honor `requires_caller_location` since it is used for much
449-
// more than just panics.
450-
self.stack().last().map_or(self.tcx.span, |f| f.current_span())
448+
M::cur_span(self)
451449
}
452450

453451
#[inline(always)]

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_middle::mir;
1111
use rustc_middle::ty::layout::TyAndLayout;
1212
use rustc_middle::ty::{self, Ty, TyCtxt};
1313
use rustc_span::def_id::DefId;
14+
use rustc_span::Span;
1415
use rustc_target::abi::{Align, Size};
1516
use rustc_target::spec::abi::Abi as CallAbi;
1617

@@ -440,6 +441,15 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
440441
frame: Frame<'mir, 'tcx, Self::Provenance>,
441442
) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>;
442443

444+
fn cur_span(ecx: &InterpCx<'mir, 'tcx, Self>) -> Span
445+
where
446+
'tcx: 'mir,
447+
{
448+
// This deliberately does *not* honor `requires_caller_location` since it is used for much
449+
// more than just panics.
450+
Self::stack(ecx).last().map_or(ecx.tcx.span, |f| f.current_span())
451+
}
452+
443453
/// Borrow the current thread's stack.
444454
fn stack<'a>(
445455
ecx: &'a InterpCx<'mir, 'tcx, Self>,

compiler/rustc_mir_dataflow/src/value_analysis.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
532532
/// places that are non-overlapping or identical.
533533
///
534534
/// The target place must have been flooded before calling this method.
535-
fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
535+
pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
536536
let StateData::Reachable(values) = &mut self.0 else { return };
537537

538538
// If both places are tracked, we copy the value to the target.
@@ -928,6 +928,31 @@ impl Map {
928928
f(v)
929929
}
930930
}
931+
932+
/// Invoke a function on each value in the given place and all descendants.
933+
pub fn for_each_projection_value<O>(
934+
&self,
935+
root: PlaceIndex,
936+
value: O,
937+
project: &mut impl FnMut(TrackElem, &O) -> Option<O>,
938+
f: &mut impl FnMut(PlaceIndex, &O),
939+
) {
940+
// Fast path is there is nothing to do.
941+
if self.inner_values[root].is_empty() {
942+
return;
943+
}
944+
945+
if self.places[root].value_index.is_some() {
946+
f(root, &value)
947+
}
948+
949+
for child in self.children(root) {
950+
let elem = self.places[child].proj_elem.unwrap();
951+
if let Some(value) = project(elem, &value) {
952+
self.for_each_projection_value(child, value, project, f);
953+
}
954+
}
955+
}
931956
}
932957

933958
/// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
//! Currently, this pass only propagates scalar values.
44
55
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};
77
use rustc_data_structures::fx::FxHashMap;
88
use rustc_hir::def::DefKind;
9+
use rustc_middle::mir::interpret::{ConstValue, Scalar};
910
use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor};
1011
use rustc_middle::mir::*;
1112
use rustc_middle::ty::layout::TyAndLayout;
1213
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
1314
use rustc_mir_dataflow::value_analysis::{
14-
Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
15+
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
1516
};
1617
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
17-
use rustc_span::DUMMY_SP;
18+
use rustc_span::{Span, DUMMY_SP};
1819
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
1920

2021
use crate::MirPass;
@@ -111,6 +112,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
111112
state: &mut State<Self::Value>,
112113
) {
113114
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+
}
114121
Rvalue::Aggregate(kind, operands) => {
115122
// If we assign `target = Enum::Variant#0(operand)`,
116123
// we must make sure that all `target as Variant#i` are `Top`.
@@ -138,8 +145,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
138145
variant_target_idx,
139146
TrackElem::Field(FieldIdx::from_usize(field_index)),
140147
) {
141-
let result = self.handle_operand(operand, state);
142-
state.insert_idx(field, result, self.map());
148+
self.assign_operand(state, field, operand);
143149
}
144150
}
145151
}
@@ -330,6 +336,86 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
330336
}
331337
}
332338

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+
333419
fn binary_op(
334420
&self,
335421
state: &mut State<FlatSet<ScalarInt>>,
@@ -604,8 +690,16 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
604690
type MemoryKind = !;
605691
const PANIC_ON_ALLOC_FAIL: bool = true;
606692

693+
#[inline(always)]
694+
fn cur_span(_ecx: &InterpCx<'mir, 'tcx, Self>) -> Span {
695+
DUMMY_SP
696+
}
697+
698+
#[inline(always)]
607699
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
609703
}
610704

611705
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
- // MIR for `constant` before DataflowConstProp
2+
+ // MIR for `constant` after DataflowConstProp
3+
4+
fn constant() -> () {
5+
let mut _0: ();
6+
let _1: E;
7+
let mut _3: isize;
8+
scope 1 {
9+
debug e => _1;
10+
let _2: i32;
11+
let _4: i32;
12+
let _5: i32;
13+
scope 2 {
14+
debug x => _2;
15+
}
16+
scope 3 {
17+
debug x => _4;
18+
}
19+
scope 4 {
20+
debug x => _5;
21+
}
22+
}
23+
24+
bb0: {
25+
StorageLive(_1);
26+
_1 = const _;
27+
StorageLive(_2);
28+
- _3 = discriminant(_1);
29+
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
30+
+ _3 = const 0_isize;
31+
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
32+
}
33+
34+
bb1: {
35+
StorageLive(_5);
36+
_5 = ((_1 as V2).0: i32);
37+
_2 = _5;
38+
StorageDead(_5);
39+
goto -> bb4;
40+
}
41+
42+
bb2: {
43+
unreachable;
44+
}
45+
46+
bb3: {
47+
StorageLive(_4);
48+
- _4 = ((_1 as V1).0: i32);
49+
- _2 = _4;
50+
+ _4 = const 0_i32;
51+
+ _2 = const 0_i32;
52+
StorageDead(_4);
53+
goto -> bb4;
54+
}
55+
56+
bb4: {
57+
_0 = const ();
58+
StorageDead(_2);
59+
StorageDead(_1);
60+
return;
61+
}
62+
}
63+

tests/mir-opt/dataflow-const-prop/enum.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ fn simple() {
1515
let x = match e { E::V1(x) => x, E::V2(x) => x };
1616
}
1717

18+
// EMIT_MIR enum.constant.DataflowConstProp.diff
19+
fn constant() {
20+
const C: E = E::V1(0);
21+
let e = C;
22+
let x = match e { E::V1(x) => x, E::V2(x) => x };
23+
}
24+
1825
#[rustc_layout_scalar_valid_range_start(1)]
1926
#[rustc_nonnull_optimization_guaranteed]
2027
struct NonZeroUsize(usize);
@@ -63,6 +70,7 @@ fn multiple(x: bool, i: u8) {
6370

6471
fn main() {
6572
simple();
73+
constant();
6674
mutate_discriminant();
6775
multiple(false, 5);
6876
}

tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@
5555
_10 = const _;
5656
StorageLive(_11);
5757
_11 = const 1_usize;
58-
_12 = Len((*_10));
58+
- _12 = Len((*_10));
5959
- _13 = Lt(_11, _12);
6060
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
61-
+ _13 = Lt(const 1_usize, _12);
62-
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable];
61+
+ _12 = const 3_usize;
62+
+ _13 = const true;
63+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable];
6364
}
6465

6566
bb2: {

tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@
5555
_10 = const _;
5656
StorageLive(_11);
5757
_11 = const 1_usize;
58-
_12 = Len((*_10));
58+
- _12 = Len((*_10));
5959
- _13 = Lt(_11, _12);
6060
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
61-
+ _13 = Lt(const 1_usize, _12);
62-
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue];
61+
+ _12 = const 3_usize;
62+
+ _13 = const true;
63+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue];
6364
}
6465

6566
bb2: {

tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@
5555
_10 = const _;
5656
StorageLive(_11);
5757
_11 = const 1_usize;
58-
_12 = Len((*_10));
58+
- _12 = Len((*_10));
5959
- _13 = Lt(_11, _12);
6060
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
61-
+ _13 = Lt(const 1_usize, _12);
62-
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable];
61+
+ _12 = const 3_usize;
62+
+ _13 = const true;
63+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable];
6364
}
6465

6566
bb2: {

tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@
5555
_10 = const _;
5656
StorageLive(_11);
5757
_11 = const 1_usize;
58-
_12 = Len((*_10));
58+
- _12 = Len((*_10));
5959
- _13 = Lt(_11, _12);
6060
- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
61-
+ _13 = Lt(const 1_usize, _12);
62-
+ assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue];
61+
+ _12 = const 3_usize;
62+
+ _13 = const true;
63+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue];
6364
}
6465

6566
bb2: {

0 commit comments

Comments
 (0)