1use itertools::Itertools as _;
55use rustc_const_eval::const_eval::DummyMachine;
56use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
57use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
58use rustc_index::IndexVec;
59use rustc_index::bit_set::{DenseBitSet, GrowableBitSet};
60use rustc_middle::bug;
61use rustc_middle::mir::interpret::Scalar;
62use rustc_middle::mir::visit::Visitor;
63use rustc_middle::mir::*;
64use rustc_middle::ty::{self, ScalarInt, TyCtxt};
65use rustc_mir_dataflow::value_analysis::{
66 Map, PlaceCollectionMode, PlaceIndex, TrackElem, ValueIndex,
67};
68use rustc_span::DUMMY_SP;
69use tracing::{debug, instrument, trace};
70
71use crate::PassPolicy;
72use crate::cost_checker::CostChecker;
73
74pub(super) struct JumpThreading;
75
76const MAX_COST: u8 = 100;
77
78impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
79 fn policy(&self, sess: &rustc_session::Session) -> PassPolicy {
80 let enabled_by_default = if sess.target.is_like_gpu {
81 false
87 } else {
88 sess.mir_opt_level() >= 2
89 };
90 PassPolicy::optimization(enabled_by_default)
91 }
92
93 #[instrument(skip_all level = "debug")]
94 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
95 let def_id = body.source.def_id();
96 debug!(?def_id);
97
98 if tcx.is_coroutine(def_id) {
100 trace!("Skipped for coroutine {:?}", def_id);
101 return;
102 }
103
104 let typing_env = body.typing_env(tcx);
105 let mut finder = TOFinder {
106 tcx,
107 typing_env,
108 ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
109 body,
110 map: Map::new(tcx, body, PlaceCollectionMode::OnDemand),
111 maybe_loop_headers: maybe_loop_headers(body),
112 entry_states: IndexVec::from_elem(ConditionSet::default(), &body.basic_blocks),
113 };
114
115 for (bb, bbdata) in traversal::postorder(body) {
116 if bbdata.is_cleanup {
117 continue;
118 }
119
120 let mut state = finder.populate_from_outgoing_edges(bb);
121 trace!("output_states[{bb:?}] = {state:?}");
122
123 finder.process_terminator(bb, &mut state);
124 trace!("pre_terminator_states[{bb:?}] = {state:?}");
125
126 for stmt in bbdata.statements.iter().rev() {
127 if state.is_empty() {
128 break;
129 }
130
131 finder.process_statement(stmt, &mut state);
132
133 if let Some((lhs, tail)) = finder.mutated_statement(stmt) {
138 finder.flood_state(lhs, tail, &mut state);
139 }
140 }
141
142 trace!("entry_states[{bb:?}] = {state:?}");
143 finder.entry_states[bb] = state;
144 }
145
146 let mut entry_states = finder.entry_states;
147 simplify_conditions(body, &mut entry_states);
148 remove_costly_conditions(tcx, typing_env, body, &mut entry_states);
149
150 if let Some(opportunities) = OpportunitySet::new(body, entry_states) {
151 opportunities.apply();
152 }
153 }
154}
155
156struct TOFinder<'a, 'tcx> {
157 tcx: TyCtxt<'tcx>,
158 typing_env: ty::TypingEnv<'tcx>,
159 ecx: InterpCx<'tcx, DummyMachine>,
160 body: &'a Body<'tcx>,
161 map: Map<'tcx>,
162 maybe_loop_headers: DenseBitSet<BasicBlock>,
163 entry_states: IndexVec<BasicBlock, ConditionSet>,
168}
169
170rustc_index::newtype_index! {
171 #[orderable]
172 #[debug_format = "_c{}"]
173 struct ConditionIndex {}
174}
175
176#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
179struct Condition {
180 place: ValueIndex,
181 value: ScalarInt,
182 polarity: Polarity,
183}
184
185#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
186enum Polarity {
187 Ne,
188 Eq,
189}
190
191impl Condition {
192 fn matches(&self, place: ValueIndex, value: ScalarInt) -> bool {
193 self.place == place && (self.value == value) == (self.polarity == Polarity::Eq)
194 }
195}
196
197#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
199enum EdgeEffect {
200 Goto { target: BasicBlock },
202 Chain { succ_block: BasicBlock, succ_condition: ConditionIndex },
204}
205
206impl EdgeEffect {
207 fn block(self) -> BasicBlock {
208 match self {
209 EdgeEffect::Goto { target: bb } | EdgeEffect::Chain { succ_block: bb, .. } => bb,
210 }
211 }
212
213 fn replace_block(&mut self, target: BasicBlock, new_target: BasicBlock) {
214 match self {
215 EdgeEffect::Goto { target: bb } | EdgeEffect::Chain { succ_block: bb, .. } => {
216 if *bb == target {
217 *bb = new_target
218 }
219 }
220 }
221 }
222}
223
224#[derive(Clone, Debug, Default)]
225struct ConditionSet {
226 active: Vec<(ConditionIndex, Condition)>,
227 fulfilled: Vec<ConditionIndex>,
228 targets: IndexVec<ConditionIndex, Vec<EdgeEffect>>,
229}
230
231impl ConditionSet {
232 fn is_empty(&self) -> bool {
233 self.active.is_empty()
234 }
235
236 #[tracing::instrument(level = "trace", skip(self))]
237 fn push_condition(&mut self, c: Condition, target: BasicBlock) {
238 let index = self.targets.push(vec![EdgeEffect::Goto { target }]);
239 self.active.push((index, c));
240 }
241
242 fn fulfill_if(&mut self, f: impl Fn(Condition, &Vec<EdgeEffect>) -> bool) {
244 self.active.retain(|&(index, condition)| {
245 let targets = &self.targets[index];
246 if f(condition, targets) {
247 trace!(?index, ?condition, "fulfill");
248 self.fulfilled.push(index);
249 false
250 } else {
251 true
252 }
253 })
254 }
255
256 fn fulfill_matches(&mut self, place: ValueIndex, value: ScalarInt) {
258 self.fulfill_if(|c, _| c.matches(place, value))
259 }
260
261 fn retain(&mut self, mut f: impl FnMut(Condition) -> bool) {
262 self.active.retain(|&(_, c)| f(c))
263 }
264
265 fn retain_mut(&mut self, mut f: impl FnMut(Condition) -> Option<Condition>) {
266 self.active.retain_mut(|(_, c)| {
267 if let Some(new) = f(*c) {
268 *c = new;
269 true
270 } else {
271 false
272 }
273 })
274 }
275
276 fn for_each_mut(&mut self, f: impl Fn(&mut Condition)) {
277 for (_, c) in &mut self.active {
278 f(c)
279 }
280 }
281}
282
283impl<'a, 'tcx> TOFinder<'a, 'tcx> {
284 fn place(&mut self, place: Place<'tcx>, tail: Option<TrackElem>) -> Option<PlaceIndex> {
285 self.map.register_place(self.tcx, self.body, place, tail)
286 }
287
288 fn value(&mut self, place: PlaceIndex) -> Option<ValueIndex> {
289 self.map.register_value(self.tcx, self.typing_env, place)
290 }
291
292 fn place_value(&mut self, place: Place<'tcx>, tail: Option<TrackElem>) -> Option<ValueIndex> {
293 let place = self.place(place, tail)?;
294 self.value(place)
295 }
296
297 #[instrument(level = "trace", skip(self))]
299 fn populate_from_outgoing_edges(&mut self, bb: BasicBlock) -> ConditionSet {
300 let bbdata = &self.body[bb];
301
302 debug_assert!(self.entry_states[bb].is_empty());
304
305 let state_len =
306 bbdata.terminator().successors().map(|succ| self.entry_states[succ].active.len()).sum();
307 let mut state = ConditionSet {
308 active: Vec::with_capacity(state_len),
309 targets: IndexVec::with_capacity(state_len),
310 fulfilled: Vec::new(),
311 };
312
313 let mut known_conditions =
315 FxIndexSet::with_capacity_and_hasher(state_len, Default::default());
316 let mut insert = |condition, succ_block, succ_condition| {
317 let (index, new) = known_conditions.insert_full(condition);
318 let index = ConditionIndex::from_usize(index);
319 if new {
320 state.active.push((index, condition));
321 let _index = state.targets.push(Vec::new());
322 debug_assert_eq!(_index, index);
323 }
324 let target = EdgeEffect::Chain { succ_block, succ_condition };
325 debug_assert!(
326 !state.targets[index].contains(&target),
327 "duplicate targets for index={index:?} as {target:?} targets={:#?}",
328 &state.targets[index],
329 );
330 state.targets[index].push(target);
331 };
332
333 let mut seen = FxHashSet::default();
335 for succ in bbdata.terminator().successors() {
336 if !seen.insert(succ) {
337 continue;
338 }
339
340 if self.maybe_loop_headers.contains(succ) {
342 continue;
343 }
344
345 for &(succ_index, cond) in self.entry_states[succ].active.iter() {
346 insert(cond, succ, succ_index);
347 }
348 }
349
350 let num_conditions = known_conditions.len();
351 debug_assert_eq!(num_conditions, state.active.len());
352 debug_assert_eq!(num_conditions, state.targets.len());
353 state.fulfilled.reserve(num_conditions);
354
355 state
356 }
357
358 fn flood_state(
360 &self,
361 place: Place<'tcx>,
362 extra_elem: Option<TrackElem>,
363 state: &mut ConditionSet,
364 ) {
365 if state.is_empty() {
366 return;
367 }
368 let mut places_to_exclude = FxHashSet::default();
369 self.map.for_each_aliasing_place(place.as_ref(), extra_elem, &mut |vi| {
370 places_to_exclude.insert(vi);
371 });
372 trace!(?places_to_exclude, "flood_state");
373 if places_to_exclude.is_empty() {
374 return;
375 }
376 state.retain(|c| !places_to_exclude.contains(&c.place));
377 }
378
379 #[instrument(level = "trace", skip(self), ret)]
393 fn mutated_statement(
394 &self,
395 stmt: &Statement<'tcx>,
396 ) -> Option<(Place<'tcx>, Option<TrackElem>)> {
397 match stmt.kind {
398 StatementKind::Assign((place, _)) => Some((place, None)),
399 StatementKind::SetDiscriminant { ref place, variant_index: _ } => {
400 Some((**place, Some(TrackElem::Discriminant)))
401 }
402 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
403 Some((Place::from(local), None))
404 }
405 | StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(..))
406 | StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(..))
408 | StatementKind::AscribeUserType(..)
409 | StatementKind::Coverage(..)
410 | StatementKind::FakeRead(..)
411 | StatementKind::ConstEvalCounter
412 | StatementKind::PlaceMention(..)
413 | StatementKind::BackwardIncompatibleDropHint { .. }
414 | StatementKind::Nop => None,
415 }
416 }
417
418 #[instrument(level = "trace", skip(self, state))]
419 fn process_immediate(&mut self, lhs: PlaceIndex, rhs: ImmTy<'tcx>, state: &mut ConditionSet) {
420 if let Some(lhs) = self.value(lhs)
421 && let Immediate::Scalar(Scalar::Int(int)) = *rhs
422 {
423 state.fulfill_matches(lhs, int)
424 }
425 }
426
427 #[instrument(level = "trace", skip(self, state))]
429 fn process_constant(
430 &mut self,
431 lhs: PlaceIndex,
432 constant: OpTy<'tcx>,
433 state: &mut ConditionSet,
434 ) {
435 self.map.for_each_projection_value(
436 lhs,
437 constant,
438 &mut |elem, op| match elem {
439 TrackElem::Field(idx) => self.ecx.project_field(op, idx).discard_err(),
440 TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(),
441 TrackElem::Discriminant => {
442 let variant = self.ecx.read_discriminant(op).discard_err()?;
443 let discr_value =
444 self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?;
445 Some(discr_value.into())
446 }
447 TrackElem::DerefLen => {
448 let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into();
449 let len_usize = op.len(&self.ecx).discard_err()?;
450 let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
451 Some(ImmTy::from_uint(len_usize, layout).into())
452 }
453 },
454 &mut |place, op| {
455 if let Some(place) = self.map.value(place)
456 && let Some(imm) = self.ecx.read_immediate_raw(op).discard_err()
457 && let Some(imm) = imm.right()
458 && let Immediate::Scalar(Scalar::Int(int)) = *imm
459 {
460 state.fulfill_matches(place, int)
461 }
462 },
463 );
464 }
465
466 #[instrument(level = "trace", skip(self, state))]
467 fn process_copy(&mut self, lhs: PlaceIndex, rhs: PlaceIndex, state: &mut ConditionSet) {
468 let mut renames = FxHashMap::default();
469 self.map.register_copy_tree(
470 lhs, rhs, &mut |lhs, rhs| {
473 renames.insert(lhs, rhs);
474 },
475 );
476 state.for_each_mut(|c| {
477 if let Some(rhs) = renames.get(&c.place) {
478 c.place = *rhs
479 }
480 });
481 }
482
483 #[instrument(level = "trace", skip(self, state))]
484 fn process_operand(&mut self, lhs: PlaceIndex, rhs: &Operand<'tcx>, state: &mut ConditionSet) {
485 match rhs {
486 Operand::Constant(constant) => {
488 let Some(constant) =
489 self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err()
490 else {
491 return;
492 };
493 self.process_constant(lhs, constant, state);
494 }
495 Operand::Move(rhs) | Operand::Copy(rhs) => {
497 let Some(rhs) = self.place(*rhs, None) else { return };
498 self.process_copy(lhs, rhs, state)
499 }
500 Operand::RuntimeChecks(_) => {}
501 }
502 }
503
504 #[instrument(level = "trace", skip(self, state))]
505 fn process_assign(
506 &mut self,
507 lhs_place: &Place<'tcx>,
508 rvalue: &Rvalue<'tcx>,
509 state: &mut ConditionSet,
510 ) {
511 let Some(lhs) = self.place(*lhs_place, None) else { return };
512 match rvalue {
513 Rvalue::Use(operand, _) => self.process_operand(lhs, operand, state),
514 Rvalue::Discriminant(rhs) => {
516 let Some(rhs) = self.place(*rhs, Some(TrackElem::Discriminant)) else { return };
517 self.process_copy(lhs, rhs, state)
518 }
519 Rvalue::Aggregate(kind, operands) => {
521 let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
522 let lhs = match kind {
523 AggregateKind::Adt(.., Some(_)) => return,
525 AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
526 let discr_ty = agg_ty.discriminant_ty(self.tcx);
527 let discr_target =
528 self.map.register_place_index(discr_ty, lhs, TrackElem::Discriminant);
529 if let Some(discr_value) =
530 self.ecx.discriminant_for_variant(agg_ty, *variant_index).discard_err()
531 {
532 self.process_immediate(discr_target, discr_value, state);
533 }
534 self.map.register_place_index(
535 agg_ty,
536 lhs,
537 TrackElem::Variant(*variant_index),
538 )
539 }
540 _ => lhs,
541 };
542 for (field_index, operand) in operands.iter_enumerated() {
543 let operand_ty = operand.ty(self.body, self.tcx);
544 let field = self.map.register_place_index(
545 operand_ty,
546 lhs,
547 TrackElem::Field(field_index),
548 );
549 self.process_operand(field, operand, state);
550 }
551 }
552 Rvalue::UnaryOp(UnOp::Not, Operand::Move(operand) | Operand::Copy(operand)) => {
554 let layout = self.ecx.layout_of(operand.ty(self.body, self.tcx).ty).unwrap();
555 let Some(lhs) = self.value(lhs) else { return };
556 let Some(operand) = self.place_value(*operand, None) else { return };
557 state.retain_mut(|mut c| {
558 if c.place == lhs {
559 let value = self
560 .ecx
561 .unary_op(UnOp::Not, &ImmTy::from_scalar_int(c.value, layout))
562 .discard_err()?
563 .to_scalar_int()
564 .discard_err()?;
565 c.place = operand;
566 c.value = value;
567 }
568 Some(c)
569 });
570 }
571 Rvalue::BinaryOp(
574 op,
575 (Operand::Move(operand) | Operand::Copy(operand), Operand::Constant(value))
576 | (Operand::Constant(value), Operand::Move(operand) | Operand::Copy(operand)),
577 ) => {
578 let equals = match op {
579 BinOp::Eq => ScalarInt::TRUE,
580 BinOp::Ne => ScalarInt::FALSE,
581 _ => return,
582 };
583 if value.const_.ty().is_floating_point() {
584 return;
589 }
590 let Some(lhs) = self.value(lhs) else { return };
591 let Some(operand) = self.place_value(*operand, None) else { return };
592 let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env)
593 else {
594 return;
595 };
596 state.for_each_mut(|c| {
597 if c.place == lhs {
598 let polarity =
599 if c.matches(lhs, equals) { Polarity::Eq } else { Polarity::Ne };
600 c.place = operand;
601 c.value = value;
602 c.polarity = polarity;
603 }
604 });
605 }
606
607 _ => {}
608 }
609 }
610
611 #[instrument(level = "trace", skip(self, state))]
612 fn process_statement(&mut self, stmt: &Statement<'tcx>, state: &mut ConditionSet) {
613 match &stmt.kind {
617 StatementKind::SetDiscriminant { place, variant_index } => {
620 let Some(discr_target) = self.place(**place, Some(TrackElem::Discriminant)) else {
621 return;
622 };
623 let enum_ty = place.ty(self.body, self.tcx).ty;
624 let Some(discr) =
628 self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
629 else {
630 return;
631 };
632 self.process_immediate(discr_target, discr, state)
633 }
634 StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(
636 Operand::Copy(place) | Operand::Move(place),
637 )) => {
638 let Some(place) = self.place_value(*place, None) else { return };
639 state.fulfill_matches(place, ScalarInt::TRUE);
640 }
641 StatementKind::Assign((lhs_place, rhs)) => self.process_assign(lhs_place, rhs, state),
642 _ => {}
643 }
644 }
645
646 #[instrument(level = "trace", skip(self, state))]
648 fn process_terminator(&mut self, bb: BasicBlock, state: &mut ConditionSet) {
649 let term = self.body.basic_blocks[bb].terminator();
650 let place_to_flood = match term.kind {
651 TerminatorKind::FalseEdge { .. }
653 | TerminatorKind::FalseUnwind { .. }
654 | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"),
655 TerminatorKind::InlineAsm { .. } => {
657 state.active.clear();
658 return;
659 }
660 TerminatorKind::SwitchInt { ref discr, ref targets } => {
662 return self.process_switch_int(discr, targets, state);
663 }
664 TerminatorKind::UnwindResume
666 | TerminatorKind::UnwindTerminate(_)
667 | TerminatorKind::Return
668 | TerminatorKind::Unreachable
669 | TerminatorKind::CoroutineDrop
670 | TerminatorKind::Assert { .. }
672 | TerminatorKind::Goto { .. } => None,
673 TerminatorKind::Drop { place: destination, .. }
675 | TerminatorKind::Call { destination, .. } => Some(destination),
676 TerminatorKind::TailCall { .. } => Some(RETURN_PLACE.into()),
677 };
678
679 if let Some(place_to_flood) = place_to_flood {
681 self.flood_state(place_to_flood, None, state);
682 }
683 }
684
685 #[instrument(level = "trace", skip(self))]
686 fn process_switch_int(
687 &mut self,
688 discr: &Operand<'tcx>,
689 targets: &SwitchTargets,
690 state: &mut ConditionSet,
691 ) {
692 let Some(discr) = discr.place() else { return };
693 let Some(discr_idx) = self.place_value(discr, None) else { return };
694
695 let discr_ty = discr.ty(self.body, self.tcx).ty;
696 let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
697
698 if targets.is_distinct() {
701 for &(index, c) in state.active.iter() {
702 if c.place != discr_idx {
703 continue;
704 }
705
706 let mut edges_fulfilling_condition = FxHashSet::default();
708
709 for (branch, tgt) in targets.iter() {
711 if let Some(branch) = ScalarInt::try_from_uint(branch, discr_layout.size)
712 && c.matches(discr_idx, branch)
713 {
714 edges_fulfilling_condition.insert(tgt);
715 }
716 }
717
718 if c.polarity == Polarity::Ne
723 && let Ok(value) = c.value.try_to_bits(discr_layout.size)
724 && targets.all_values().contains(&value.into())
725 {
726 edges_fulfilling_condition.insert(targets.otherwise());
727 }
728
729 let condition_targets = &state.targets[index];
733
734 let new_edges: Vec<_> = condition_targets
735 .iter()
736 .copied()
737 .filter(|&target| match target {
738 EdgeEffect::Goto { .. } => false,
739 EdgeEffect::Chain { succ_block, .. } => {
740 edges_fulfilling_condition.contains(&succ_block)
741 }
742 })
743 .collect();
744
745 if new_edges.len() == condition_targets.len() {
746 state.fulfilled.push(index);
749 } else {
750 let index = state.targets.push(new_edges);
753 state.fulfilled.push(index);
754 }
755 }
756 }
757
758 let mut mk_condition = |value, polarity, target| {
760 let c = Condition { place: discr_idx, value, polarity };
761 state.push_condition(c, target);
762 };
763 if let Some((value, then_, else_)) = targets.as_static_if() {
764 let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
766 mk_condition(value, Polarity::Eq, then_);
767 mk_condition(value, Polarity::Ne, else_);
768 } else {
769 for (value, target) in targets.iter() {
772 if let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) {
773 mk_condition(value, Polarity::Eq, target);
774 }
775 }
776 }
777 }
778}
779
780#[instrument(level = "debug", skip(body, entry_states))]
782fn simplify_conditions(body: &Body<'_>, entry_states: &mut IndexVec<BasicBlock, ConditionSet>) {
783 let basic_blocks = &body.basic_blocks;
784 let reverse_postorder = basic_blocks.reverse_postorder();
785
786 let mut predecessors = IndexVec::from_elem(0, &entry_states);
789 predecessors[START_BLOCK] = 1; for &bb in reverse_postorder {
791 let term = basic_blocks[bb].terminator();
792 for s in term.successors() {
793 predecessors[s] += 1;
794 }
795 }
796
797 let mut fulfill_in_pred_count = IndexVec::from_fn_n(
799 |bb: BasicBlock| IndexVec::from_elem_n(0, entry_states[bb].targets.len()),
800 entry_states.len(),
801 );
802
803 for &bb in reverse_postorder {
805 let preds = predecessors[bb];
806 trace!(?bb, ?preds);
807
808 if preds == 0 {
810 continue;
811 }
812
813 let state = &mut entry_states[bb];
814 trace!(?state);
815
816 trace!(fulfilled_count = ?fulfill_in_pred_count[bb]);
818 for (condition, &cond_preds) in fulfill_in_pred_count[bb].iter_enumerated() {
819 if cond_preds == preds {
820 trace!(?condition);
821 state.fulfilled.push(condition);
822 }
823 }
824
825 let mut targets: Vec<_> = state
828 .fulfilled
829 .iter()
830 .flat_map(|&index| state.targets[index].iter().copied())
831 .collect();
832 targets.sort();
833 targets.dedup();
834 trace!(?targets);
835
836 let mut successors = basic_blocks[bb].terminator().successors().collect::<Vec<_>>();
838
839 targets.reverse();
840 while let Some(target) = targets.pop() {
841 match target {
842 EdgeEffect::Goto { target } => {
843 predecessors[target] += 1;
846 for &s in successors.iter() {
847 predecessors[s] -= 1;
848 }
849 targets.retain(|t| t.block() == target);
851 successors.clear();
852 successors.push(target);
853 }
854 EdgeEffect::Chain { succ_block, succ_condition } => {
855 let count = successors.iter().filter(|&&s| s == succ_block).count();
858 fulfill_in_pred_count[succ_block][succ_condition] += count;
859 }
860 }
861 }
862 }
863}
864
865#[instrument(level = "debug", skip(tcx, typing_env, body, entry_states))]
866fn remove_costly_conditions<'tcx>(
867 tcx: TyCtxt<'tcx>,
868 typing_env: ty::TypingEnv<'tcx>,
869 body: &Body<'tcx>,
870 entry_states: &mut IndexVec<BasicBlock, ConditionSet>,
871) {
872 let basic_blocks = &body.basic_blocks;
873
874 let mut costs = IndexVec::from_elem(None, basic_blocks);
875 let mut cost = |bb: BasicBlock| -> u8 {
876 let c = *costs[bb].get_or_insert_with(|| {
877 let bbdata = &basic_blocks[bb];
878 let mut cost = CostChecker::new(tcx, typing_env, None, body);
879 cost.visit_basic_block_data(bb, bbdata);
880 cost.cost().try_into().unwrap_or(MAX_COST)
881 });
882 trace!("cost[{bb:?}] = {c}");
883 c
884 };
885
886 let mut condition_cost = IndexVec::from_fn_n(
888 |bb: BasicBlock| IndexVec::from_elem_n(MAX_COST, entry_states[bb].targets.len()),
889 entry_states.len(),
890 );
891
892 let reverse_postorder = basic_blocks.reverse_postorder();
893
894 for &bb in reverse_postorder.iter().rev() {
895 let state = &entry_states[bb];
896 trace!(?bb, ?state);
897
898 let mut current_costs = IndexVec::from_elem(0u8, &state.targets);
899
900 for (condition, targets) in state.targets.iter_enumerated() {
901 for &target in targets {
902 match target {
903 EdgeEffect::Goto { .. } => {}
905 EdgeEffect::Chain { succ_block, succ_condition }
907 if entry_states[succ_block].fulfilled.contains(&succ_condition) => {}
908 EdgeEffect::Chain { succ_block, succ_condition } => {
910 let duplication_cost = cost(succ_block);
912 let target_cost =
914 *condition_cost[succ_block].get(succ_condition).unwrap_or(&MAX_COST);
915 let cost = current_costs[condition]
916 .saturating_add(duplication_cost)
917 .saturating_add(target_cost);
918 trace!(?condition, ?succ_block, ?duplication_cost, ?target_cost);
919 current_costs[condition] = cost;
920 }
921 }
922 }
923 }
924
925 trace!("condition_cost[{bb:?}] = {:?}", current_costs);
926 condition_cost[bb] = current_costs;
927 }
928
929 trace!(?condition_cost);
930
931 for &bb in reverse_postorder {
932 for (index, targets) in entry_states[bb].targets.iter_enumerated_mut() {
933 if condition_cost[bb][index] >= MAX_COST {
934 trace!(?bb, ?index, ?targets, c = ?condition_cost[bb][index], "remove");
935 targets.clear()
936 }
937 }
938 }
939}
940
941struct OpportunitySet<'a, 'tcx> {
942 basic_blocks: &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
943 entry_states: IndexVec<BasicBlock, ConditionSet>,
944 duplicates: FxHashMap<(BasicBlock, ConditionIndex), BasicBlock>,
947}
948
949impl<'a, 'tcx> OpportunitySet<'a, 'tcx> {
950 fn new(
951 body: &'a mut Body<'tcx>,
952 mut entry_states: IndexVec<BasicBlock, ConditionSet>,
953 ) -> Option<OpportunitySet<'a, 'tcx>> {
954 trace!(def_id = ?body.source.def_id(), "apply");
955
956 if entry_states.iter().all(|state| state.fulfilled.is_empty()) {
957 return None;
958 }
959
960 for state in entry_states.iter_mut() {
962 state.active = Default::default();
963 }
964 let duplicates = Default::default();
965 let basic_blocks = body.basic_blocks.as_mut();
966 Some(OpportunitySet { basic_blocks, entry_states, duplicates })
967 }
968
969 #[instrument(level = "debug", skip(self))]
971 fn apply(mut self) {
972 let mut worklist = Vec::with_capacity(self.basic_blocks.len());
973 worklist.push(START_BLOCK);
974
975 let mut visited = GrowableBitSet::with_capacity(self.basic_blocks.len());
977
978 while let Some(bb) = worklist.pop() {
979 if !visited.insert(bb) {
980 continue;
981 }
982
983 self.apply_once(bb);
984
985 worklist.extend(self.basic_blocks[bb].terminator().successors());
988 }
989 }
990
991 #[instrument(level = "debug", skip(self))]
993 fn apply_once(&mut self, bb: BasicBlock) {
994 let state = &mut self.entry_states[bb];
995 trace!(?state);
996
997 let mut targets: Vec<_> = state
1000 .fulfilled
1001 .iter()
1002 .flat_map(|&index| std::mem::take(&mut state.targets[index]))
1003 .collect();
1004 targets.sort();
1005 targets.dedup();
1006 trace!(?targets);
1007
1008 targets.reverse();
1010 while let Some(target) = targets.pop() {
1011 debug!(?target);
1012 trace!(term = ?self.basic_blocks[bb].terminator().kind);
1013
1014 debug_assert!(
1018 self.basic_blocks[bb].terminator().successors().contains(&target.block()),
1019 "missing {target:?} in successors for {bb:?}, term={:?}",
1020 self.basic_blocks[bb].terminator(),
1021 );
1022
1023 match target {
1024 EdgeEffect::Goto { target } => {
1025 self.apply_goto(bb, target);
1026
1027 targets.retain(|t| t.block() == target);
1029 for ts in self.entry_states[bb].targets.iter_mut() {
1031 ts.retain(|t| t.block() == target);
1032 }
1033 }
1034 EdgeEffect::Chain { succ_block, succ_condition } => {
1035 let new_succ_block = self.apply_chain(bb, succ_block, succ_condition);
1036
1037 if let Some(new_succ_block) = new_succ_block {
1039 for t in targets.iter_mut() {
1040 t.replace_block(succ_block, new_succ_block)
1041 }
1042 for t in
1044 self.entry_states[bb].targets.iter_mut().flat_map(|ts| ts.iter_mut())
1045 {
1046 t.replace_block(succ_block, new_succ_block)
1047 }
1048 }
1049 }
1050 }
1051
1052 trace!(post_term = ?self.basic_blocks[bb].terminator().kind);
1053 }
1054 }
1055
1056 #[instrument(level = "debug", skip(self))]
1057 fn apply_goto(&mut self, bb: BasicBlock, target: BasicBlock) {
1058 self.basic_blocks[bb].terminator_mut().kind = TerminatorKind::Goto { target };
1059 }
1060
1061 #[instrument(level = "debug", skip(self), ret)]
1062 fn apply_chain(
1063 &mut self,
1064 bb: BasicBlock,
1065 target: BasicBlock,
1066 condition: ConditionIndex,
1067 ) -> Option<BasicBlock> {
1068 if self.entry_states[target].fulfilled.contains(&condition) {
1069 trace!("fulfilled");
1071 return None;
1072 }
1073
1074 let new_target = *self.duplicates.entry((target, condition)).or_insert_with(|| {
1080 let new_target = self.basic_blocks.push(self.basic_blocks[target].clone());
1083 trace!(?target, ?new_target, ?condition, "clone");
1084
1085 let mut condition_set = self.entry_states[target].clone();
1088 condition_set.fulfilled.push(condition);
1089 let _new_target = self.entry_states.push(condition_set);
1090 debug_assert_eq!(new_target, _new_target);
1091
1092 new_target
1093 });
1094 trace!(?target, ?new_target, ?condition, "reuse");
1095
1096 self.basic_blocks[bb].terminator_mut().successors_mut(|s| {
1099 if *s == target {
1100 *s = new_target;
1101 }
1102 });
1103
1104 Some(new_target)
1105 }
1106}
1107
1108fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
1114 let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
1115 let mut visited = DenseBitSet::new_empty(body.basic_blocks.len());
1116 for (bb, bbdata) in traversal::postorder(body) {
1117 for succ in bbdata.terminator().successors() {
1120 if !visited.contains(succ) {
1121 maybe_loop_headers.insert(succ);
1122 }
1123 }
1124
1125 let _new = visited.insert(bb);
1128 debug_assert!(_new);
1129 }
1130
1131 maybe_loop_headers
1132}