|
1 | 1 | use rustc_index::bit_set::BitSet;
|
2 | 2 | use rustc_middle::mir::{self, BasicBlock, Location};
|
3 |
| -use rustc_middle::ty::{self, TyCtxt}; |
| 3 | +use rustc_middle::ty::TyCtxt; |
4 | 4 | use std::ops::RangeInclusive;
|
5 | 5 |
|
6 | 6 | use super::visitor::{ResultsVisitable, ResultsVisitor};
|
7 |
| -use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet}; |
| 7 | +use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget}; |
8 | 8 |
|
9 | 9 | pub trait Direction {
|
10 | 10 | fn is_forward() -> bool;
|
@@ -425,8 +425,8 @@ impl Direction for Forward {
|
425 | 425 |
|
426 | 426 | fn join_state_into_successors_of<A>(
|
427 | 427 | analysis: &A,
|
428 |
| - tcx: TyCtxt<'tcx>, |
429 |
| - body: &mir::Body<'tcx>, |
| 428 | + _tcx: TyCtxt<'tcx>, |
| 429 | + _body: &mir::Body<'tcx>, |
430 | 430 | dead_unwinds: Option<&BitSet<BasicBlock>>,
|
431 | 431 | exit_state: &mut A::Domain,
|
432 | 432 | (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
@@ -489,88 +489,78 @@ impl Direction for Forward {
|
489 | 489 | }
|
490 | 490 |
|
491 | 491 | SwitchInt { ref targets, ref values, ref discr, switch_ty: _ } => {
|
492 |
| - let enum_ = discr |
493 |
| - .place() |
494 |
| - .and_then(|discr| switch_on_enum_discriminant(tcx, &body, bb_data, discr)); |
495 |
| - match enum_ { |
496 |
| - // If this is a switch on an enum discriminant, a custom effect may be applied |
497 |
| - // along each outgoing edge. |
498 |
| - Some((enum_place, enum_def)) => { |
499 |
| - // MIR building adds discriminants to the `values` array in the same order as they |
500 |
| - // are yielded by `AdtDef::discriminants`. We rely on this to match each |
501 |
| - // discriminant in `values` to its corresponding variant in linear time. |
502 |
| - let mut tmp = analysis.bottom_value(body); |
503 |
| - let mut discriminants = enum_def.discriminants(tcx); |
504 |
| - for (value, target) in values.iter().zip(targets.iter().copied()) { |
505 |
| - let (variant_idx, _) = |
506 |
| - discriminants.find(|&(_, discr)| discr.val == *value).expect( |
507 |
| - "Order of `AdtDef::discriminants` differed \ |
508 |
| - from that of `SwitchInt::values`", |
509 |
| - ); |
510 |
| - |
511 |
| - tmp.clone_from(exit_state); |
512 |
| - analysis.apply_discriminant_switch_effect( |
513 |
| - &mut tmp, |
514 |
| - bb, |
515 |
| - enum_place, |
516 |
| - enum_def, |
517 |
| - variant_idx, |
518 |
| - ); |
519 |
| - propagate(target, &tmp); |
520 |
| - } |
521 |
| - |
522 |
| - // Move out of `tmp` so we don't accidentally use it below. |
523 |
| - std::mem::drop(tmp); |
524 |
| - |
525 |
| - // Propagate dataflow state along the "otherwise" edge. |
526 |
| - let otherwise = targets.last().copied().unwrap(); |
527 |
| - propagate(otherwise, exit_state) |
528 |
| - } |
529 |
| - |
530 |
| - // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same |
531 |
| - // exit state. |
532 |
| - None => { |
533 |
| - for target in targets.iter().copied() { |
534 |
| - propagate(target, exit_state); |
535 |
| - } |
| 492 | + let mut applier = SwitchIntEdgeEffectApplier { |
| 493 | + exit_state, |
| 494 | + targets: targets.as_ref(), |
| 495 | + values: values.as_ref(), |
| 496 | + propagate, |
| 497 | + effects_applied: false, |
| 498 | + }; |
| 499 | + |
| 500 | + analysis.apply_switch_int_edge_effects(bb, discr, &mut applier); |
| 501 | + |
| 502 | + let SwitchIntEdgeEffectApplier { |
| 503 | + exit_state, mut propagate, effects_applied, .. |
| 504 | + } = applier; |
| 505 | + |
| 506 | + if !effects_applied { |
| 507 | + for &target in targets.iter() { |
| 508 | + propagate(target, exit_state); |
536 | 509 | }
|
537 | 510 | }
|
538 | 511 | }
|
539 | 512 | }
|
540 | 513 | }
|
541 | 514 | }
|
542 | 515 |
|
543 |
| -/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is |
544 |
| -/// an enum discriminant. |
545 |
| -/// |
546 |
| -/// We expect such blocks to have a call to `discriminant` as their last statement like so: |
547 |
| -/// _42 = discriminant(_1) |
548 |
| -/// SwitchInt(_42, ..) |
549 |
| -/// |
550 |
| -/// If the basic block matches this pattern, this function returns the place corresponding to the |
551 |
| -/// enum (`_1` in the example above) as well as the `AdtDef` of that enum. |
552 |
| -fn switch_on_enum_discriminant( |
553 |
| - tcx: TyCtxt<'tcx>, |
554 |
| - body: &'mir mir::Body<'tcx>, |
555 |
| - block: &'mir mir::BasicBlockData<'tcx>, |
556 |
| - switch_on: mir::Place<'tcx>, |
557 |
| -) -> Option<(mir::Place<'tcx>, &'tcx ty::AdtDef)> { |
558 |
| - match block.statements.last().map(|stmt| &stmt.kind) { |
559 |
| - Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) |
560 |
| - if *lhs == switch_on => |
561 |
| - { |
562 |
| - match &discriminated.ty(body, tcx).ty.kind() { |
563 |
| - ty::Adt(def, _) => Some((*discriminated, def)), |
564 |
| - |
565 |
| - // `Rvalue::Discriminant` is also used to get the active yield point for a |
566 |
| - // generator, but we do not need edge-specific effects in that case. This may |
567 |
| - // change in the future. |
568 |
| - ty::Generator(..) => None, |
569 |
| - |
570 |
| - t => bug!("`discriminant` called on unexpected type {:?}", t), |
571 |
| - } |
| 516 | +struct SwitchIntEdgeEffectApplier<'a, D, F> { |
| 517 | + exit_state: &'a mut D, |
| 518 | + values: &'a [u128], |
| 519 | + targets: &'a [BasicBlock], |
| 520 | + propagate: F, |
| 521 | + |
| 522 | + effects_applied: bool, |
| 523 | +} |
| 524 | + |
| 525 | +impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F> |
| 526 | +where |
| 527 | + D: Clone, |
| 528 | + F: FnMut(BasicBlock, &D), |
| 529 | +{ |
| 530 | + fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { |
| 531 | + assert!(!self.effects_applied); |
| 532 | + |
| 533 | + let mut tmp = None; |
| 534 | + for (&value, &target) in self.values.iter().zip(self.targets.iter()) { |
| 535 | + let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state); |
| 536 | + apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target }); |
| 537 | + (self.propagate)(target, tmp); |
572 | 538 | }
|
573 | 539 |
|
574 |
| - _ => None, |
| 540 | + // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`, |
| 541 | + // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state. |
| 542 | + let otherwise = self.targets.last().copied().unwrap(); |
| 543 | + apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise }); |
| 544 | + (self.propagate)(otherwise, self.exit_state); |
| 545 | + |
| 546 | + self.effects_applied = true; |
| 547 | + } |
| 548 | +} |
| 549 | + |
| 550 | +/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses |
| 551 | +/// the more efficient `clone_from` if `opt` was `Some`. |
| 552 | +/// |
| 553 | +/// Returns a mutable reference to the new clone that resides in `opt`. |
| 554 | +// |
| 555 | +// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the |
| 556 | +// standard library? |
| 557 | +fn opt_clone_from_or_clone<T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T { |
| 558 | + if opt.is_some() { |
| 559 | + let ret = opt.as_mut().unwrap(); |
| 560 | + ret.clone_from(val); |
| 561 | + ret |
| 562 | + } else { |
| 563 | + *opt = Some(val.clone()); |
| 564 | + opt.as_mut().unwrap() |
575 | 565 | }
|
576 | 566 | }
|
0 commit comments