about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform')
-rw-r--r--compiler/rustc_mir_transform/messages.ftl2
-rw-r--r--compiler/rustc_mir_transform/src/check_inline.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs240
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs119
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs5
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs6
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs8
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs15
8 files changed, 18 insertions, 383 deletions
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index ae3062f07de..2e08f50e8a9 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -9,8 +9,6 @@ mir_transform_const_mut_borrow = taking a mutable reference to a `const` item
     .note2 = the mutable reference will refer to this temporary, not the original `const` item
     .note3 = mutable reference created due to call to this method
 
-mir_transform_exceeds_mcdc_test_vector_limit = number of total test vectors in one function will exceed limit ({$max_num_test_vectors}) if this decision is instrumented, so MC/DC analysis ignores it
-
 mir_transform_ffi_unwind_call = call to {$foreign ->
     [true] foreign function
     *[false] function pointer
diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs
index af6da209081..8d28cb3ca00 100644
--- a/compiler/rustc_mir_transform/src/check_inline.rs
+++ b/compiler/rustc_mir_transform/src/check_inline.rs
@@ -45,12 +45,6 @@ pub(super) fn is_inline_valid_on_fn<'tcx>(
         return Err("#[rustc_no_mir_inline]");
     }
 
-    // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined
-    // MIR correctly when Modified Condition/Decision Coverage is enabled.
-    if tcx.sess.instrument_coverage_mcdc() {
-        return Err("incompatible with MC/DC coverage");
-    }
-
     let ty = tcx.type_of(def_id);
     if match ty.instantiate_identity().kind() {
         ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(),
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index b0e24cf2bdb..399978b5915 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -1,10 +1,5 @@
-use std::collections::BTreeSet;
-
-use rustc_data_structures::fx::FxIndexMap;
 use rustc_index::IndexVec;
-use rustc_middle::mir::coverage::{
-    BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageInfoHi, CoverageKind,
-};
+use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind};
 use rustc_middle::mir::{self, BasicBlock, StatementKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
@@ -13,7 +8,6 @@ use crate::coverage::ExtractedHirInfo;
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 use crate::coverage::spans::extract_refined_covspans;
 use crate::coverage::unexpand::unexpand_into_body_span;
-use crate::errors::MCDCExceedsTestVectorLimit;
 
 /// Associates an ordinary executable code span with its corresponding BCB.
 #[derive(Debug)]
@@ -22,9 +16,6 @@ pub(super) struct CodeMapping {
     pub(super) bcb: BasicCoverageBlock,
 }
 
-/// This is separate from [`MCDCBranch`] to help prepare for larger changes
-/// that will be needed for improved branch coverage in the future.
-/// (See <https://github.com/rust-lang/rust/pull/124217>.)
 #[derive(Debug)]
 pub(super) struct BranchPair {
     pub(super) span: Span,
@@ -32,40 +23,10 @@ pub(super) struct BranchPair {
     pub(super) false_bcb: BasicCoverageBlock,
 }
 
-/// Associates an MC/DC branch span with condition info besides fields for normal branch.
-#[derive(Debug)]
-pub(super) struct MCDCBranch {
-    pub(super) span: Span,
-    pub(super) true_bcb: BasicCoverageBlock,
-    pub(super) false_bcb: BasicCoverageBlock,
-    pub(super) condition_info: ConditionInfo,
-    // Offset added to test vector idx if this branch is evaluated to true.
-    pub(super) true_index: usize,
-    // Offset added to test vector idx if this branch is evaluated to false.
-    pub(super) false_index: usize,
-}
-
-/// Associates an MC/DC decision with its join BCBs.
-#[derive(Debug)]
-pub(super) struct MCDCDecision {
-    pub(super) span: Span,
-    pub(super) end_bcbs: BTreeSet<BasicCoverageBlock>,
-    pub(super) bitmap_idx: usize,
-    pub(super) num_test_vectors: usize,
-    pub(super) decision_depth: u16,
-}
-
-// LLVM uses `i32` to index the bitmap. Thus `i32::MAX` is the hard limit for number of all test vectors
-// in a function.
-const MCDC_MAX_BITMAP_SIZE: usize = i32::MAX as usize;
-
 #[derive(Default)]
 pub(super) struct ExtractedMappings {
     pub(super) code_mappings: Vec<CodeMapping>,
     pub(super) branch_pairs: Vec<BranchPair>,
-    pub(super) mcdc_bitmap_bits: usize,
-    pub(super) mcdc_degraded_branches: Vec<MCDCBranch>,
-    pub(super) mcdc_mappings: Vec<(MCDCDecision, Vec<MCDCBranch>)>,
 }
 
 /// Extracts coverage-relevant spans from MIR, and associates them with
@@ -78,32 +39,13 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
 ) -> ExtractedMappings {
     let mut code_mappings = vec![];
     let mut branch_pairs = vec![];
-    let mut mcdc_bitmap_bits = 0;
-    let mut mcdc_degraded_branches = vec![];
-    let mut mcdc_mappings = vec![];
 
     // Extract ordinary code mappings from MIR statement/terminator spans.
     extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
 
     branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
 
-    extract_mcdc_mappings(
-        mir_body,
-        tcx,
-        hir_info.body_span,
-        graph,
-        &mut mcdc_bitmap_bits,
-        &mut mcdc_degraded_branches,
-        &mut mcdc_mappings,
-    );
-
-    ExtractedMappings {
-        code_mappings,
-        branch_pairs,
-        mcdc_bitmap_bits,
-        mcdc_degraded_branches,
-        mcdc_mappings,
-    }
+    ExtractedMappings { code_mappings, branch_pairs }
 }
 
 fn resolve_block_markers(
@@ -127,12 +69,6 @@ fn resolve_block_markers(
     block_markers
 }
 
-// FIXME: There is currently a lot of redundancy between
-// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
-// that they can each be modified without interfering with the other, but in
-// the long term we should try to bring them together again when branch coverage
-// and MC/DC coverage support are more mature.
-
 pub(super) fn extract_branch_pairs(
     mir_body: &mir::Body<'_>,
     hir_info: &ExtractedHirInfo,
@@ -162,175 +98,3 @@ pub(super) fn extract_branch_pairs(
         })
         .collect::<Vec<_>>()
 }
-
-pub(super) fn extract_mcdc_mappings(
-    mir_body: &mir::Body<'_>,
-    tcx: TyCtxt<'_>,
-    body_span: Span,
-    graph: &CoverageGraph,
-    mcdc_bitmap_bits: &mut usize,
-    mcdc_degraded_branches: &mut impl Extend<MCDCBranch>,
-    mcdc_mappings: &mut impl Extend<(MCDCDecision, Vec<MCDCBranch>)>,
-) {
-    let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return };
-
-    let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
-
-    let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?);
-
-    let check_branch_bcb =
-        |raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
-            // For now, ignore any branch span that was introduced by
-            // expansion. This makes things like assert macros less noisy.
-            if !raw_span.ctxt().outer_expn_data().is_root() {
-                return None;
-            }
-            let span = unexpand_into_body_span(raw_span, body_span)?;
-
-            let true_bcb = bcb_from_marker(true_marker)?;
-            let false_bcb = bcb_from_marker(false_marker)?;
-            Some((span, true_bcb, false_bcb))
-        };
-
-    let to_mcdc_branch = |&mir::coverage::MCDCBranchSpan {
-                              span: raw_span,
-                              condition_info,
-                              true_marker,
-                              false_marker,
-                          }| {
-        let (span, true_bcb, false_bcb) = check_branch_bcb(raw_span, true_marker, false_marker)?;
-        Some(MCDCBranch {
-            span,
-            true_bcb,
-            false_bcb,
-            condition_info,
-            true_index: usize::MAX,
-            false_index: usize::MAX,
-        })
-    };
-
-    let mut get_bitmap_idx = |num_test_vectors: usize| -> Option<usize> {
-        let bitmap_idx = *mcdc_bitmap_bits;
-        let next_bitmap_bits = bitmap_idx.saturating_add(num_test_vectors);
-        (next_bitmap_bits <= MCDC_MAX_BITMAP_SIZE).then(|| {
-            *mcdc_bitmap_bits = next_bitmap_bits;
-            bitmap_idx
-        })
-    };
-    mcdc_degraded_branches
-        .extend(coverage_info_hi.mcdc_degraded_branch_spans.iter().filter_map(to_mcdc_branch));
-
-    mcdc_mappings.extend(coverage_info_hi.mcdc_spans.iter().filter_map(|(decision, branches)| {
-        if branches.len() == 0 {
-            return None;
-        }
-        let decision_span = unexpand_into_body_span(decision.span, body_span)?;
-
-        let end_bcbs = decision
-            .end_markers
-            .iter()
-            .map(|&marker| bcb_from_marker(marker))
-            .collect::<Option<_>>()?;
-        let mut branch_mappings: Vec<_> = branches.into_iter().filter_map(to_mcdc_branch).collect();
-        if branch_mappings.len() != branches.len() {
-            mcdc_degraded_branches.extend(branch_mappings);
-            return None;
-        }
-        let num_test_vectors = calc_test_vectors_index(&mut branch_mappings);
-        let Some(bitmap_idx) = get_bitmap_idx(num_test_vectors) else {
-            tcx.dcx().emit_warn(MCDCExceedsTestVectorLimit {
-                span: decision_span,
-                max_num_test_vectors: MCDC_MAX_BITMAP_SIZE,
-            });
-            mcdc_degraded_branches.extend(branch_mappings);
-            return None;
-        };
-        // LLVM requires span of the decision contains all spans of its conditions.
-        // Usually the decision span meets the requirement well but in cases like macros it may not.
-        let span = branch_mappings
-            .iter()
-            .map(|branch| branch.span)
-            .reduce(|lhs, rhs| lhs.to(rhs))
-            .map(
-                |joint_span| {
-                    if decision_span.contains(joint_span) { decision_span } else { joint_span }
-                },
-            )
-            .expect("branch mappings are ensured to be non-empty as checked above");
-        Some((
-            MCDCDecision {
-                span,
-                end_bcbs,
-                bitmap_idx,
-                num_test_vectors,
-                decision_depth: decision.decision_depth,
-            },
-            branch_mappings,
-        ))
-    }));
-}
-
-// LLVM checks the executed test vector by accumulating indices of tested branches.
-// We calculate number of all possible test vectors of the decision and assign indices
-// to branches here.
-// See [the rfc](https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798/)
-// for more details about the algorithm.
-// This function is mostly like [`TVIdxBuilder::TvIdxBuilder`](https://github.com/llvm/llvm-project/blob/d594d9f7f4dc6eb748b3261917db689fdc348b96/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp#L226)
-fn calc_test_vectors_index(conditions: &mut Vec<MCDCBranch>) -> usize {
-    let mut indegree_stats = IndexVec::<ConditionId, usize>::from_elem_n(0, conditions.len());
-    // `num_paths` is `width` described at the llvm rfc, which indicates how many paths reaching the condition node.
-    let mut num_paths_stats = IndexVec::<ConditionId, usize>::from_elem_n(0, conditions.len());
-    let mut next_conditions = conditions
-        .iter_mut()
-        .map(|branch| {
-            let ConditionInfo { condition_id, true_next_id, false_next_id } = branch.condition_info;
-            [true_next_id, false_next_id]
-                .into_iter()
-                .flatten()
-                .for_each(|next_id| indegree_stats[next_id] += 1);
-            (condition_id, branch)
-        })
-        .collect::<FxIndexMap<_, _>>();
-
-    let mut queue =
-        std::collections::VecDeque::from_iter(next_conditions.swap_remove(&ConditionId::START));
-    num_paths_stats[ConditionId::START] = 1;
-    let mut decision_end_nodes = Vec::new();
-    while let Some(branch) = queue.pop_front() {
-        let ConditionInfo { condition_id, true_next_id, false_next_id } = branch.condition_info;
-        let (false_index, true_index) = (&mut branch.false_index, &mut branch.true_index);
-        let this_paths_count = num_paths_stats[condition_id];
-        // Note. First check the false next to ensure conditions are touched in same order with llvm-cov.
-        for (next, index) in [(false_next_id, false_index), (true_next_id, true_index)] {
-            if let Some(next_id) = next {
-                let next_paths_count = &mut num_paths_stats[next_id];
-                *index = *next_paths_count;
-                *next_paths_count = next_paths_count.saturating_add(this_paths_count);
-                let next_indegree = &mut indegree_stats[next_id];
-                *next_indegree -= 1;
-                if *next_indegree == 0 {
-                    queue.push_back(next_conditions.swap_remove(&next_id).expect(
-                        "conditions with non-zero indegree before must be in next_conditions",
-                    ));
-                }
-            } else {
-                decision_end_nodes.push((this_paths_count, condition_id, index));
-            }
-        }
-    }
-    assert!(next_conditions.is_empty(), "the decision tree has untouched nodes");
-    let mut cur_idx = 0;
-    // LLVM hopes the end nodes are sorted in descending order by `num_paths` so that it can
-    // optimize bitmap size for decisions in tree form such as `a && b && c && d && ...`.
-    decision_end_nodes.sort_by_key(|(num_paths, _, _)| usize::MAX - *num_paths);
-    for (num_paths, condition_id, index) in decision_end_nodes {
-        assert_eq!(
-            num_paths, num_paths_stats[condition_id],
-            "end nodes should not be updated since they were visited"
-        );
-        assert_eq!(*index, usize::MAX, "end nodes should not be assigned index before");
-        *index = cur_idx;
-        cur_idx += num_paths;
-    }
-    cur_idx
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index f253d1662ca..f6945a95a7c 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -10,9 +10,7 @@ mod unexpand;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_middle::hir::nested_filter;
-use rustc_middle::mir::coverage::{
-    CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,
-};
+use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo, Mapping, MappingKind};
 use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
@@ -95,14 +93,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
 
     // Inject coverage statements into MIR.
     inject_coverage_statements(mir_body, &graph);
-    inject_mcdc_statements(mir_body, &graph, &extracted_mappings);
-
-    let mcdc_num_condition_bitmaps = extracted_mappings
-        .mcdc_mappings
-        .iter()
-        .map(|&(mappings::MCDCDecision { decision_depth, .. }, _)| decision_depth)
-        .max()
-        .map_or(0, |max| usize::from(max) + 1);
 
     mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
         function_source_hash: hir_info.function_source_hash,
@@ -111,9 +101,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
         priority_list,
 
         mappings,
-
-        mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
-        mcdc_num_condition_bitmaps,
     }));
 }
 
@@ -124,13 +111,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
 /// function can potentially be simplified even further.
 fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
     // Fully destructure the mappings struct to make sure we don't miss any kinds.
-    let ExtractedMappings {
-        code_mappings,
-        branch_pairs,
-        mcdc_bitmap_bits: _,
-        mcdc_degraded_branches,
-        mcdc_mappings,
-    } = extracted_mappings;
+    let ExtractedMappings { code_mappings, branch_pairs } = extracted_mappings;
     let mut mappings = Vec::new();
 
     mappings.extend(code_mappings.iter().map(
@@ -148,57 +129,6 @@ fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
         },
     ));
 
-    // MCDC branch mappings are appended with their decisions in case decisions were ignored.
-    mappings.extend(mcdc_degraded_branches.iter().map(
-        |&mappings::MCDCBranch {
-             span,
-             true_bcb,
-             false_bcb,
-             condition_info: _,
-             true_index: _,
-             false_index: _,
-         }| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } },
-    ));
-
-    for (decision, branches) in mcdc_mappings {
-        // FIXME(#134497): Previously it was possible for some of these branch
-        // conversions to fail, in which case the remaining branches in the
-        // decision would be degraded to plain `MappingKind::Branch`.
-        // The changes in #134497 made that failure impossible, because the
-        // fallible step was deferred to codegen. But the corresponding code
-        // in codegen wasn't updated to detect the need for a degrade step.
-        let conditions = branches
-            .into_iter()
-            .map(
-                |&mappings::MCDCBranch {
-                     span,
-                     true_bcb,
-                     false_bcb,
-                     condition_info,
-                     true_index: _,
-                     false_index: _,
-                 }| {
-                    Mapping {
-                        kind: MappingKind::MCDCBranch {
-                            true_bcb,
-                            false_bcb,
-                            mcdc_params: condition_info,
-                        },
-                        span,
-                    }
-                },
-            )
-            .collect::<Vec<_>>();
-
-        // LLVM requires end index for counter mapping regions.
-        let kind = MappingKind::MCDCDecision(DecisionInfo {
-            bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32,
-            num_conditions: u16::try_from(conditions.len()).unwrap(),
-        });
-        let span = decision.span;
-        mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter()));
-    }
-
     mappings
 }
 
@@ -210,51 +140,6 @@ fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &Cove
     }
 }
 
-/// For each conditions inject statements to update condition bitmap after it has been evaluated.
-/// For each decision inject statements to update test vector bitmap after it has been evaluated.
-fn inject_mcdc_statements<'tcx>(
-    mir_body: &mut mir::Body<'tcx>,
-    graph: &CoverageGraph,
-    extracted_mappings: &ExtractedMappings,
-) {
-    for (decision, conditions) in &extracted_mappings.mcdc_mappings {
-        // Inject test vector update first because `inject_statement` always insert new statement at head.
-        for &end in &decision.end_bcbs {
-            let end_bb = graph[end].leader_bb();
-            inject_statement(
-                mir_body,
-                CoverageKind::TestVectorBitmapUpdate {
-                    bitmap_idx: decision.bitmap_idx as u32,
-                    decision_depth: decision.decision_depth,
-                },
-                end_bb,
-            );
-        }
-
-        for &mappings::MCDCBranch {
-            span: _,
-            true_bcb,
-            false_bcb,
-            condition_info: _,
-            true_index,
-            false_index,
-        } in conditions
-        {
-            for (index, bcb) in [(false_index, false_bcb), (true_index, true_bcb)] {
-                let bb = graph[bcb].leader_bb();
-                inject_statement(
-                    mir_body,
-                    CoverageKind::CondBitmapUpdate {
-                        index: index as u32,
-                        decision_depth: decision.decision_depth,
-                    },
-                    bb,
-                );
-            }
-        }
-    }
-}
-
 fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) {
     debug!("  injecting statement {counter_kind:?} for {bb:?}");
     let data = &mut mir_body[bb];
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index c195ca51540..63c550c27fe 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -111,11 +111,6 @@ fn coverage_ids_info<'tcx>(
                 bcb_needs_counter.insert(true_bcb);
                 bcb_needs_counter.insert(false_bcb);
             }
-            MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params: _ } => {
-                bcb_needs_counter.insert(true_bcb);
-                bcb_needs_counter.insert(false_bcb);
-            }
-            MappingKind::MCDCDecision(_) => {}
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 804cd8ab3f7..7985e1c0798 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -101,11 +101,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
         StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None,
 
         // These coverage statements should not exist prior to coverage instrumentation.
-        StatementKind::Coverage(
-            CoverageKind::VirtualCounter { .. }
-            | CoverageKind::CondBitmapUpdate { .. }
-            | CoverageKind::TestVectorBitmapUpdate { .. },
-        ) => bug!(
+        StatementKind::Coverage(CoverageKind::VirtualCounter { .. }) => bug!(
             "Unexpected coverage statement found during coverage instrumentation: {statement:?}"
         ),
     }
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index cffa0183fa7..ad9635aae33 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -117,14 +117,6 @@ pub(crate) struct FnItemRef {
     pub ident: Ident,
 }
 
-#[derive(Diagnostic)]
-#[diag(mir_transform_exceeds_mcdc_test_vector_limit)]
-pub(crate) struct MCDCExceedsTestVectorLimit {
-    #[primary_span]
-    pub(crate) span: Span,
-    pub(crate) max_num_test_vectors: usize,
-}
-
 pub(crate) struct MustNotSupend<'a, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
     pub yield_sp: Span,
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index dc99b67a1e8..952da2cdf72 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -756,7 +756,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 && let Some(v) = self.simplify_place_value(&mut pointee, location)
             {
                 value = v;
-                place_ref = pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
+                // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
+                // That local is SSA, but we otherwise have no guarantee on that local's value at
+                // the current location compared to its value where `pointee` was borrowed.
+                if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
+                    place_ref =
+                        pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
+                }
             }
             if let Some(local) = self.try_as_local(value, location) {
                 // Both `local` and `Place { local: place.local, projection: projection[..index] }`
@@ -774,7 +780,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             && let Some(v) = self.simplify_place_value(&mut pointee, location)
         {
             value = v;
-            place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
+            // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
+            // That local is SSA, but we otherwise have no guarantee on that local's value at
+            // the current location compared to its value where `pointee` was borrowed.
+            if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
+                place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
+            }
         }
         if let Some(new_local) = self.try_as_local(value, location) {
             place_ref = PlaceRef { local: new_local, projection: &[] };