Skip to content

Commit 82f0468

Browse files
committed
Handle reading statics.
1 parent 6ad6b43 commit 82f0468

File tree

5 files changed

+276
-40
lines changed

5 files changed

+276
-40
lines changed

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_const_eval::const_eval::CheckAlignment;
66
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};
9+
use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ConstValue, InterpResult, Scalar};
1010
use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor};
1111
use rustc_middle::mir::*;
1212
use rustc_middle::ty::layout::TyAndLayout;
@@ -15,6 +15,7 @@ use rustc_mir_dataflow::value_analysis::{
1515
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
1616
};
1717
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
18+
use rustc_span::def_id::DefId;
1819
use rustc_span::{Span, DUMMY_SP};
1920
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
2021

@@ -78,7 +79,7 @@ struct ConstAnalysis<'a, 'tcx> {
7879
}
7980

8081
impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
81-
type Value = FlatSet<ScalarInt>;
82+
type Value = FlatSet<Scalar>;
8283

8384
const NAME: &'static str = "ConstAnalysis";
8485

@@ -182,7 +183,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
182183
if let Some(overflow_target) = overflow_target {
183184
let overflow = match overflow {
184185
FlatSet::Top => FlatSet::Top,
185-
FlatSet::Elem(overflow) => FlatSet::Elem(overflow.into()),
186+
FlatSet::Elem(overflow) => FlatSet::Elem(Scalar::from_bool(overflow)),
186187
FlatSet::Bottom => FlatSet::Bottom,
187188
};
188189
// We have flooded `target` earlier.
@@ -204,7 +205,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
204205
&& let ty::Array(_, len) = operand_ty.ty.kind()
205206
&& let Some(len) = ConstantKind::Ty(*len).eval(self.tcx, self.param_env).try_to_scalar_int()
206207
{
207-
state.insert_value_idx(target_len, FlatSet::Elem(len), self.map());
208+
state.insert_value_idx(target_len, FlatSet::Elem(len.into()), self.map());
208209
}
209210
}
210211
_ => self.super_assign(target, rvalue, state),
@@ -222,7 +223,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
222223
if let ty::Array(_, len) = place_ty.ty.kind() {
223224
ConstantKind::Ty(*len)
224225
.eval(self.tcx, self.param_env)
225-
.try_to_scalar_int()
226+
.try_to_scalar()
226227
.map_or(FlatSet::Top, FlatSet::Elem)
227228
} else if let [ProjectionElem::Deref] = place.projection[..] {
228229
state.get_len(place.local.into(), self.map())
@@ -281,7 +282,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
281282
.bytes(),
282283
_ => return ValueOrPlace::Value(FlatSet::Top),
283284
};
284-
ScalarInt::try_from_target_usize(val, self.tcx).map_or(FlatSet::Top, FlatSet::Elem)
285+
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
285286
}
286287
Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), self.map()),
287288
_ => return self.super_rvalue(rvalue, state),
@@ -297,7 +298,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
297298
constant
298299
.literal
299300
.eval(self.tcx, self.param_env)
300-
.try_to_scalar_int()
301+
.try_to_scalar()
301302
.map_or(FlatSet::Top, FlatSet::Elem)
302303
}
303304

@@ -339,14 +340,21 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
339340
/// The caller must have flooded `place`.
340341
fn assign_operand(
341342
&self,
342-
state: &mut State<FlatSet<ScalarInt>>,
343+
state: &mut State<FlatSet<Scalar>>,
343344
place: PlaceIndex,
344345
operand: &Operand<'tcx>,
345346
) {
346347
match operand {
347348
Operand::Copy(rhs) | Operand::Move(rhs) => {
348349
if let Some(rhs) = self.map.find(rhs.as_ref()) {
349-
state.insert_place_idx(place, rhs, &self.map)
350+
state.insert_place_idx(place, rhs, &self.map);
351+
} else if rhs.projection.first() == Some(&PlaceElem::Deref)
352+
&& let FlatSet::Elem(pointer) = state.get(rhs.local.into(), &self.map)
353+
&& let rhs_ty = self.local_decls[rhs.local].ty
354+
&& let Ok(rhs_layout) = self.tcx.layout_of(self.param_env.and(rhs_ty))
355+
{
356+
let op = ImmTy::from_scalar(pointer, rhs_layout).into();
357+
self.assign_constant(state, place, op, &rhs.projection);
350358
}
351359
}
352360
Operand::Constant(box constant) => {
@@ -363,15 +371,15 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
363371
#[instrument(level = "trace", skip(self, state))]
364372
fn assign_constant(
365373
&self,
366-
state: &mut State<FlatSet<ScalarInt>>,
374+
state: &mut State<FlatSet<Scalar>>,
367375
place: PlaceIndex,
368376
mut operand: OpTy<'tcx>,
369377
projection: &[PlaceElem<'tcx>],
370378
) -> Option<!> {
371379
for &(mut proj_elem) in projection {
372380
if let PlaceElem::Index(index) = proj_elem {
373381
if let FlatSet::Elem(index) = state.get(index.into(), &self.map)
374-
&& let Ok(offset) = index.try_to_target_usize(self.tcx)
382+
&& let Ok(offset) = index.to_target_usize(&self.tcx)
375383
&& let Some(min_length) = offset.checked_add(1)
376384
{
377385
proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false };
@@ -406,7 +414,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
406414
&mut |place, op| {
407415
if let Ok(imm) = self.ecx.read_immediate_raw(op)
408416
&& let Some(imm) = imm.right()
409-
&& let Immediate::Scalar(Scalar::Int(scalar)) = *imm
417+
&& let Immediate::Scalar(scalar) = *imm
410418
{
411419
state.insert_value_idx(place, FlatSet::Elem(scalar), &self.map);
412420
}
@@ -418,11 +426,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
418426

419427
fn binary_op(
420428
&self,
421-
state: &mut State<FlatSet<ScalarInt>>,
429+
state: &mut State<FlatSet<Scalar>>,
422430
op: BinOp,
423431
left: &Operand<'tcx>,
424432
right: &Operand<'tcx>,
425-
) -> (FlatSet<ScalarInt>, FlatSet<bool>) {
433+
) -> (FlatSet<Scalar>, FlatSet<bool>) {
426434
let left = self.eval_operand(left, state);
427435
let right = self.eval_operand(right, state);
428436

@@ -431,9 +439,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
431439
// Both sides are known, do the actual computation.
432440
(FlatSet::Elem(left), FlatSet::Elem(right)) => {
433441
match self.ecx.overflowing_binary_op(op, &left, &right) {
434-
Ok((Scalar::Int(val), overflow, _)) => {
435-
(FlatSet::Elem(val), FlatSet::Elem(overflow))
436-
}
442+
Ok((val, overflow, _)) => (FlatSet::Elem(val), FlatSet::Elem(overflow)),
437443
_ => (FlatSet::Top, FlatSet::Top),
438444
}
439445
}
@@ -445,9 +451,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
445451
}
446452

447453
let arg_scalar = const_arg.to_scalar();
448-
let Ok(arg_scalar) = arg_scalar.try_to_int() else {
449-
return (FlatSet::Top, FlatSet::Top);
450-
};
451454
let Ok(arg_value) = arg_scalar.to_bits(layout.size) else {
452455
return (FlatSet::Top, FlatSet::Top);
453456
};
@@ -473,7 +476,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
473476
fn eval_operand(
474477
&self,
475478
op: &Operand<'tcx>,
476-
state: &mut State<FlatSet<ScalarInt>>,
479+
state: &mut State<FlatSet<Scalar>>,
477480
) -> FlatSet<ImmTy<'tcx>> {
478481
let value = match self.handle_operand(op, state) {
479482
ValueOrPlace::Value(value) => value,
@@ -492,24 +495,24 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
492495
}
493496
}
494497

495-
fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option<ScalarInt> {
498+
fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option<Scalar> {
496499
if !enum_ty.is_enum() {
497500
return None;
498501
}
499502
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
500503
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
501-
let discr_value = ScalarInt::try_from_uint(discr.val, discr_layout.size)?;
504+
let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
502505
Some(discr_value)
503506
}
504507

505-
fn wrap_immediate(&self, imm: Immediate) -> FlatSet<ScalarInt> {
508+
fn wrap_immediate(&self, imm: Immediate) -> FlatSet<Scalar> {
506509
match imm {
507-
Immediate::Scalar(Scalar::Int(scalar)) => FlatSet::Elem(scalar),
510+
Immediate::Scalar(scalar) => FlatSet::Elem(scalar),
508511
_ => FlatSet::Top,
509512
}
510513
}
511514

512-
fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<ScalarInt> {
515+
fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<Scalar> {
513516
self.wrap_immediate(*val)
514517
}
515518
}
@@ -550,7 +553,7 @@ impl<'mir, 'tcx>
550553
ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
551554
for CollectAndPatch<'tcx, '_>
552555
{
553-
type FlowState = State<FlatSet<ScalarInt>>;
556+
type FlowState = State<FlatSet<Scalar>>;
554557

555558
fn visit_statement_before_primary_effect(
556559
&mut self,
@@ -580,14 +583,10 @@ impl<'mir, 'tcx>
580583
// Don't overwrite the assignment if it already uses a constant (to keep the span).
581584
}
582585
StatementKind::Assign(box (place, _)) => {
583-
match state.get(place.as_ref(), &results.analysis.0.map) {
584-
FlatSet::Top => (),
585-
FlatSet::Elem(value) => {
586-
self.assignments.insert(location, value);
587-
}
588-
FlatSet::Bottom => {
589-
// This assignment is either unreachable, or an uninitialized value is assigned.
590-
}
586+
if let FlatSet::Elem(Scalar::Int(value)) =
587+
state.get(place.as_ref(), &results.analysis.0.map)
588+
{
589+
self.assignments.insert(location, value);
591590
}
592591
}
593592
_ => (),
@@ -657,15 +656,15 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx, '_> {
657656
}
658657

659658
struct OperandCollector<'tcx, 'map, 'locals, 'a> {
660-
state: &'a State<FlatSet<ScalarInt>>,
659+
state: &'a State<FlatSet<Scalar>>,
661660
visitor: &'a mut CollectAndPatch<'tcx, 'locals>,
662661
map: &'map Map,
663662
}
664663

665664
impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
666665
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
667666
if let Some(place) = operand.place() {
668-
if let FlatSet::Elem(value) = self.state.get(place.as_ref(), self.map) {
667+
if let FlatSet::Elem(Scalar::Int(value)) = self.state.get(place.as_ref(), self.map) {
669668
self.visitor.before_effect.insert((location, place), value);
670669
} else if !place.projection.is_empty() {
671670
// Try to propagate into `Index` projections.
@@ -676,7 +675,7 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
676675

677676
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, location: Location) {
678677
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy | NonMutatingUseContext::Move) = ctxt
679-
&& let FlatSet::Elem(value) = self.state.get(local.into(), self.map)
678+
&& let FlatSet::Elem(Scalar::Int(value)) = self.state.get(local.into(), self.map)
680679
{
681680
self.visitor.before_effect.insert((location, local.into()), value);
682681
}
@@ -685,6 +684,34 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
685684

686685
struct DummyMachine;
687686

687+
/// Macro for machine-specific `InterpError` without allocation.
688+
/// (These will never be shown to the user, but they help diagnose ICEs.)
689+
macro_rules! throw_machine_stop_str {
690+
($($tt:tt)*) => {{
691+
// We make a new local type for it. The type itself does not carry any information,
692+
// but its vtable (for the `MachineStopType` trait) does.
693+
#[derive(Debug)]
694+
struct Zst;
695+
// Printing this type shows the desired string.
696+
impl std::fmt::Display for Zst {
697+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
698+
write!(f, $($tt)*)
699+
}
700+
}
701+
impl rustc_middle::mir::interpret::MachineStopType for Zst {
702+
fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
703+
self.to_string().into()
704+
}
705+
706+
fn add_args(
707+
self: Box<Self>,
708+
_: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
709+
) {}
710+
}
711+
throw_machine_stop!(Zst)
712+
}};
713+
}
714+
688715
impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine {
689716
rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>);
690717
type MemoryKind = !;
@@ -714,6 +741,27 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
714741
unimplemented!()
715742
}
716743

744+
fn before_access_global(
745+
_tcx: TyCtxt<'tcx>,
746+
_machine: &Self,
747+
_alloc_id: AllocId,
748+
alloc: ConstAllocation<'tcx>,
749+
_static_def_id: Option<DefId>,
750+
is_write: bool,
751+
) -> InterpResult<'tcx> {
752+
if is_write {
753+
throw_machine_stop_str!("can't write to global");
754+
}
755+
756+
// If the static allocation is mutable, then we can't const prop it as its content
757+
// might be different at runtime.
758+
if alloc.inner().mutability.is_mut() {
759+
throw_machine_stop_str!("can't access mutable globals in ConstProp");
760+
}
761+
762+
Ok(())
763+
}
764+
717765
fn find_mir_or_eval_fn(
718766
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
719767
_instance: ty::Instance<'tcx>,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::intrinsics::mir::*;
66

7+
#[derive(Copy, Clone)]
78
enum E {
89
V1(i32),
910
V2(i32)
@@ -22,6 +23,17 @@ fn constant() {
2223
let x = match e { E::V1(x) => x, E::V2(x) => x };
2324
}
2425

26+
// EMIT_MIR enum.statics.DataflowConstProp.diff
27+
fn statics() {
28+
static C: E = E::V1(0);
29+
let e = C;
30+
let x = match e { E::V1(x) => x, E::V2(x) => x };
31+
32+
static RC: &E = &E::V2(4);
33+
let e = RC;
34+
let x = match e { E::V1(x) => x, E::V2(x) => x };
35+
}
36+
2537
#[rustc_layout_scalar_valid_range_start(1)]
2638
#[rustc_nonnull_optimization_guaranteed]
2739
struct NonZeroUsize(usize);
@@ -71,6 +83,7 @@ fn multiple(x: bool, i: u8) {
7183
fn main() {
7284
simple();
7385
constant();
86+
statics();
7487
mutate_discriminant();
7588
multiple(false, 5);
7689
}

0 commit comments

Comments
 (0)