Skip to content

Commit e96cc85

Browse files
authored
Unrolled build for #119461
Rollup merge of #119461 - cjgillot:jump-threading-interp, r=tmiasko Use an interpreter in MIR jump threading This allows to understand assignments of aggregate constants. This case appears more frequently with GVN promoting aggregates to constants.
2 parents 867d39c + e72b2b1 commit e96cc85

File tree

4 files changed

+277
-107
lines changed

4 files changed

+277
-107
lines changed

compiler/rustc_mir_transform/src/jump_threading.rs

+155-107
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@
3636
//! cost by `MAX_COST`.
3737
3838
use rustc_arena::DroplessArena;
39+
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
3940
use rustc_data_structures::fx::FxHashSet;
4041
use rustc_index::bit_set::BitSet;
4142
use rustc_index::IndexVec;
43+
use rustc_middle::mir::interpret::Scalar;
4244
use rustc_middle::mir::visit::Visitor;
4345
use rustc_middle::mir::*;
44-
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
46+
use rustc_middle::ty::layout::LayoutOf;
47+
use rustc_middle::ty::{self, ScalarInt, TyCtxt};
4548
use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem};
49+
use rustc_span::DUMMY_SP;
4650
use rustc_target::abi::{TagEncoding, Variants};
4751

4852
use crate::cost_checker::CostChecker;
53+
use crate::dataflow_const_prop::DummyMachine;
4954

5055
pub struct JumpThreading;
5156

@@ -71,6 +76,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
7176
let mut finder = TOFinder {
7277
tcx,
7378
param_env,
79+
ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
7480
body,
7581
arena: &arena,
7682
map: &map,
@@ -88,7 +94,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
8894
debug!(?discr, ?bb);
8995

9096
let discr_ty = discr.ty(body, tcx).ty;
91-
let Ok(discr_layout) = tcx.layout_of(param_env.and(discr_ty)) else { continue };
97+
let Ok(discr_layout) = finder.ecx.layout_of(discr_ty) else { continue };
9298

9399
let Some(discr) = finder.map.find(discr.as_ref()) else { continue };
94100
debug!(?discr);
@@ -142,6 +148,7 @@ struct ThreadingOpportunity {
142148
struct TOFinder<'tcx, 'a> {
143149
tcx: TyCtxt<'tcx>,
144150
param_env: ty::ParamEnv<'tcx>,
151+
ecx: InterpCx<'tcx, 'tcx, DummyMachine>,
145152
body: &'a Body<'tcx>,
146153
map: &'a Map,
147154
loop_headers: &'a BitSet<BasicBlock>,
@@ -329,25 +336,82 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
329336
}
330337

331338
#[instrument(level = "trace", skip(self))]
332-
fn process_operand(
339+
fn process_immediate(
333340
&mut self,
334341
bb: BasicBlock,
335342
lhs: PlaceIndex,
336-
rhs: &Operand<'tcx>,
343+
rhs: ImmTy<'tcx>,
337344
state: &mut State<ConditionSet<'a>>,
338345
) -> Option<!> {
339346
let register_opportunity = |c: Condition| {
340347
debug!(?bb, ?c.target, "register");
341348
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
342349
};
343350

351+
let conditions = state.try_get_idx(lhs, self.map)?;
352+
if let Immediate::Scalar(Scalar::Int(int)) = *rhs {
353+
conditions.iter_matches(int).for_each(register_opportunity);
354+
}
355+
356+
None
357+
}
358+
359+
/// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
360+
#[instrument(level = "trace", skip(self))]
361+
fn process_constant(
362+
&mut self,
363+
bb: BasicBlock,
364+
lhs: PlaceIndex,
365+
constant: OpTy<'tcx>,
366+
state: &mut State<ConditionSet<'a>>,
367+
) {
368+
self.map.for_each_projection_value(
369+
lhs,
370+
constant,
371+
&mut |elem, op| match elem {
372+
TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(),
373+
TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(),
374+
TrackElem::Discriminant => {
375+
let variant = self.ecx.read_discriminant(op).ok()?;
376+
let discr_value =
377+
self.ecx.discriminant_for_variant(op.layout.ty, variant).ok()?;
378+
Some(discr_value.into())
379+
}
380+
TrackElem::DerefLen => {
381+
let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into();
382+
let len_usize = op.len(&self.ecx).ok()?;
383+
let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
384+
Some(ImmTy::from_uint(len_usize, layout).into())
385+
}
386+
},
387+
&mut |place, op| {
388+
if let Some(conditions) = state.try_get_idx(place, self.map)
389+
&& let Ok(imm) = self.ecx.read_immediate_raw(op)
390+
&& let Some(imm) = imm.right()
391+
&& let Immediate::Scalar(Scalar::Int(int)) = *imm
392+
{
393+
conditions.iter_matches(int).for_each(|c: Condition| {
394+
self.opportunities
395+
.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
396+
})
397+
}
398+
},
399+
);
400+
}
401+
402+
#[instrument(level = "trace", skip(self))]
403+
fn process_operand(
404+
&mut self,
405+
bb: BasicBlock,
406+
lhs: PlaceIndex,
407+
rhs: &Operand<'tcx>,
408+
state: &mut State<ConditionSet<'a>>,
409+
) -> Option<!> {
344410
match rhs {
345411
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
346412
Operand::Constant(constant) => {
347-
let conditions = state.try_get_idx(lhs, self.map)?;
348-
let constant =
349-
constant.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?;
350-
conditions.iter_matches(constant).for_each(register_opportunity);
413+
let constant = self.ecx.eval_mir_constant(&constant.const_, None, None).ok()?;
414+
self.process_constant(bb, lhs, constant, state);
351415
}
352416
// Transfer the conditions on the copied rhs.
353417
Operand::Move(rhs) | Operand::Copy(rhs) => {
@@ -359,6 +423,84 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
359423
None
360424
}
361425

426+
#[instrument(level = "trace", skip(self))]
427+
fn process_assign(
428+
&mut self,
429+
bb: BasicBlock,
430+
lhs_place: &Place<'tcx>,
431+
rhs: &Rvalue<'tcx>,
432+
state: &mut State<ConditionSet<'a>>,
433+
) -> Option<!> {
434+
let lhs = self.map.find(lhs_place.as_ref())?;
435+
match rhs {
436+
Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
437+
// Transfer the conditions on the copy rhs.
438+
Rvalue::CopyForDeref(rhs) => {
439+
self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
440+
}
441+
Rvalue::Discriminant(rhs) => {
442+
let rhs = self.map.find_discr(rhs.as_ref())?;
443+
state.insert_place_idx(rhs, lhs, self.map);
444+
}
445+
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
446+
Rvalue::Aggregate(box ref kind, ref operands) => {
447+
let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
448+
let lhs = match kind {
449+
// Do not support unions.
450+
AggregateKind::Adt(.., Some(_)) => return None,
451+
AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
452+
if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant)
453+
&& let Ok(discr_value) =
454+
self.ecx.discriminant_for_variant(agg_ty, *variant_index)
455+
{
456+
self.process_immediate(bb, discr_target, discr_value, state);
457+
}
458+
self.map.apply(lhs, TrackElem::Variant(*variant_index))?
459+
}
460+
_ => lhs,
461+
};
462+
for (field_index, operand) in operands.iter_enumerated() {
463+
if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) {
464+
self.process_operand(bb, field, operand, state);
465+
}
466+
}
467+
}
468+
// Transfer the conditions on the copy rhs, after inversing polarity.
469+
Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
470+
let conditions = state.try_get_idx(lhs, self.map)?;
471+
let place = self.map.find(place.as_ref())?;
472+
let conds = conditions.map(self.arena, Condition::inv);
473+
state.insert_value_idx(place, conds, self.map);
474+
}
475+
// We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
476+
// Create a condition on `rhs ?= B`.
477+
Rvalue::BinaryOp(
478+
op,
479+
box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value))
480+
| box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)),
481+
) => {
482+
let conditions = state.try_get_idx(lhs, self.map)?;
483+
let place = self.map.find(place.as_ref())?;
484+
let equals = match op {
485+
BinOp::Eq => ScalarInt::TRUE,
486+
BinOp::Ne => ScalarInt::FALSE,
487+
_ => return None,
488+
};
489+
let value = value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?;
490+
let conds = conditions.map(self.arena, |c| Condition {
491+
value,
492+
polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne },
493+
..c
494+
});
495+
state.insert_value_idx(place, conds, self.map);
496+
}
497+
498+
_ => {}
499+
}
500+
501+
None
502+
}
503+
362504
#[instrument(level = "trace", skip(self))]
363505
fn process_statement(
364506
&mut self,
@@ -374,18 +516,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
374516
// Below, `lhs` is the return value of `mutated_statement`,
375517
// the place to which `conditions` apply.
376518

377-
let discriminant_for_variant = |enum_ty: Ty<'tcx>, variant_index| {
378-
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
379-
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
380-
let scalar = ScalarInt::try_from_uint(discr.val, discr_layout.size)?;
381-
Some(Operand::const_from_scalar(
382-
self.tcx,
383-
discr.ty,
384-
scalar.into(),
385-
rustc_span::DUMMY_SP,
386-
))
387-
};
388-
389519
match &stmt.kind {
390520
// If we expect `discriminant(place) ?= A`,
391521
// we have an opportunity if `variant_index ?= A`.
@@ -395,7 +525,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
395525
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
396526
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
397527
// nothing.
398-
let enum_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?;
528+
let enum_layout = self.ecx.layout_of(enum_ty).ok()?;
399529
let writes_discriminant = match enum_layout.variants {
400530
Variants::Single { index } => {
401531
assert_eq!(index, *variant_index);
@@ -408,8 +538,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
408538
} => *variant_index != untagged_variant,
409539
};
410540
if writes_discriminant {
411-
let discr = discriminant_for_variant(enum_ty, *variant_index)?;
412-
self.process_operand(bb, discr_target, &discr, state)?;
541+
let discr = self.ecx.discriminant_for_variant(enum_ty, *variant_index).ok()?;
542+
self.process_immediate(bb, discr_target, discr, state)?;
413543
}
414544
}
415545
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
@@ -420,89 +550,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
420550
conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity);
421551
}
422552
StatementKind::Assign(box (lhs_place, rhs)) => {
423-
if let Some(lhs) = self.map.find(lhs_place.as_ref()) {
424-
match rhs {
425-
Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
426-
// Transfer the conditions on the copy rhs.
427-
Rvalue::CopyForDeref(rhs) => {
428-
self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
429-
}
430-
Rvalue::Discriminant(rhs) => {
431-
let rhs = self.map.find_discr(rhs.as_ref())?;
432-
state.insert_place_idx(rhs, lhs, self.map);
433-
}
434-
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
435-
Rvalue::Aggregate(box ref kind, ref operands) => {
436-
let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
437-
let lhs = match kind {
438-
// Do not support unions.
439-
AggregateKind::Adt(.., Some(_)) => return None,
440-
AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
441-
if let Some(discr_target) =
442-
self.map.apply(lhs, TrackElem::Discriminant)
443-
&& let Some(discr_value) =
444-
discriminant_for_variant(agg_ty, *variant_index)
445-
{
446-
self.process_operand(bb, discr_target, &discr_value, state);
447-
}
448-
self.map.apply(lhs, TrackElem::Variant(*variant_index))?
449-
}
450-
_ => lhs,
451-
};
452-
for (field_index, operand) in operands.iter_enumerated() {
453-
if let Some(field) =
454-
self.map.apply(lhs, TrackElem::Field(field_index))
455-
{
456-
self.process_operand(bb, field, operand, state);
457-
}
458-
}
459-
}
460-
// Transfer the conditions on the copy rhs, after inversing polarity.
461-
Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
462-
let conditions = state.try_get_idx(lhs, self.map)?;
463-
let place = self.map.find(place.as_ref())?;
464-
let conds = conditions.map(self.arena, Condition::inv);
465-
state.insert_value_idx(place, conds, self.map);
466-
}
467-
// We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
468-
// Create a condition on `rhs ?= B`.
469-
Rvalue::BinaryOp(
470-
op,
471-
box (
472-
Operand::Move(place) | Operand::Copy(place),
473-
Operand::Constant(value),
474-
)
475-
| box (
476-
Operand::Constant(value),
477-
Operand::Move(place) | Operand::Copy(place),
478-
),
479-
) => {
480-
let conditions = state.try_get_idx(lhs, self.map)?;
481-
let place = self.map.find(place.as_ref())?;
482-
let equals = match op {
483-
BinOp::Eq => ScalarInt::TRUE,
484-
BinOp::Ne => ScalarInt::FALSE,
485-
_ => return None,
486-
};
487-
let value = value
488-
.const_
489-
.normalize(self.tcx, self.param_env)
490-
.try_to_scalar_int()?;
491-
let conds = conditions.map(self.arena, |c| Condition {
492-
value,
493-
polarity: if c.matches(equals) {
494-
Polarity::Eq
495-
} else {
496-
Polarity::Ne
497-
},
498-
..c
499-
});
500-
state.insert_value_idx(place, conds, self.map);
501-
}
502-
503-
_ => {}
504-
}
505-
}
553+
self.process_assign(bb, lhs_place, rhs, state)?;
506554
}
507555
_ => {}
508556
}
@@ -577,7 +625,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
577625

578626
let discr = discr.place()?;
579627
let discr_ty = discr.ty(self.body, self.tcx).ty;
580-
let discr_layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?;
628+
let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
581629
let conditions = state.try_get(discr.as_ref(), self.map)?;
582630

583631
if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) {

0 commit comments

Comments
 (0)