Skip to content

Commit f49126e

Browse files
committed
Document wf constraints on control flow in cleanup blocks
Also fixes a bug in dominator computation
1 parent 4781233 commit f49126e

File tree

3 files changed

+68
-6
lines changed

3 files changed

+68
-6
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+57-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Validates the MIR to ensure that invariants are upheld.
22
3-
use rustc_data_structures::fx::FxHashSet;
3+
use std::collections::hash_map::Entry;
4+
5+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
46
use rustc_index::bit_set::BitSet;
57
use rustc_infer::traits::Reveal;
68
use rustc_middle::mir::interpret::Scalar;
@@ -18,7 +20,7 @@ use rustc_mir_dataflow::storage::always_storage_live_locals;
1820
use rustc_mir_dataflow::{Analysis, ResultsCursor};
1921
use rustc_target::abi::{Size, VariantIdx};
2022

21-
#[derive(Copy, Clone, Debug)]
23+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2224
enum EdgeKind {
2325
Unwind,
2426
Normal,
@@ -57,7 +59,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
5759
.iterate_to_fixpoint()
5860
.into_results_cursor(body);
5961

60-
TypeChecker {
62+
let mut checker = TypeChecker {
6163
when: &self.when,
6264
body,
6365
tcx,
@@ -67,8 +69,9 @@ impl<'tcx> MirPass<'tcx> for Validator {
6769
storage_liveness,
6870
place_cache: Vec::new(),
6971
value_cache: Vec::new(),
70-
}
71-
.visit_body(body);
72+
};
73+
checker.visit_body(body);
74+
checker.check_cleanup_control_flow();
7275
}
7376
}
7477

@@ -134,6 +137,55 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
134137
}
135138
}
136139

140+
fn check_cleanup_control_flow(&self) {
141+
let doms = self.body.basic_blocks.dominators();
142+
let mut post_contract_node = FxHashMap::default();
143+
let mut get_post_contract_node = |mut bb| {
144+
if let Some(res) = post_contract_node.get(&bb) {
145+
return *res;
146+
}
147+
let mut dom_path = vec![];
148+
while self.body.basic_blocks[bb].is_cleanup {
149+
dom_path.push(bb);
150+
bb = doms.immediate_dominator(bb);
151+
}
152+
let root = *dom_path.last().unwrap();
153+
for bb in dom_path {
154+
post_contract_node.insert(bb, root);
155+
}
156+
root
157+
};
158+
159+
let mut parent = FxHashMap::default();
160+
for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
161+
if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
162+
continue;
163+
}
164+
let bb = get_post_contract_node(bb);
165+
for s in bb_data.terminator().successors() {
166+
let s = get_post_contract_node(s);
167+
if s == bb {
168+
continue;
169+
}
170+
match parent.entry(bb) {
171+
Entry::Vacant(e) => {
172+
e.insert(s);
173+
}
174+
Entry::Occupied(e) if s != *e.get() => self.fail(
175+
Location { block: bb, statement_index: 0 },
176+
format!(
177+
"Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
178+
bb,
179+
s,
180+
*e.get()
181+
)
182+
),
183+
Entry::Occupied(_) => (),
184+
}
185+
}
186+
}
187+
}
188+
137189
/// Check if src can be assigned into dest.
138190
/// This is not precise, it will accept some incorrect assignments.
139191
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {

compiler/rustc_data_structures/src/graph/dominators/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
135135
// This loop computes the semi[w] for w.
136136
semi[w] = w;
137137
for v in graph.predecessors(pre_order_to_real[w]) {
138-
let v = real_to_pre_order[v].unwrap();
138+
// Reachable vertices may have unreachable predecessors, so ignore any of them
139+
let Some(v) = real_to_pre_order[v] else {
140+
continue
141+
};
139142

140143
// eval returns a vertex x from which semi[x] is minimum among
141144
// vertices semi[v] +> x *> v.

compiler/rustc_middle/src/mir/syntax.rs

+7
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,13 @@ pub struct CopyNonOverlapping<'tcx> {
512512
/// must also be `cleanup`. This is a part of the type system and checked statically, so it is
513513
/// still an error to have such an edge in the CFG even if it's known that it won't be taken at
514514
/// runtime.
515+
/// 4. The induced subgraph on cleanup blocks must look roughly like an upside down tree. This is
516+
/// necessary to ensure that landing pad information can be correctly codegened. More precisely:
517+
///
518+
/// Begin with the standard control flow graph `G`. Modify `G` as follows: for any two cleanup
519+
/// vertices `u` and `v` such that `u` dominates `v`, contract `u` and `v` into a single vertex,
520+
/// deleting self edges and duplicate edges in the process. The cleanup blocks of the resulting
521+
/// graph must form an inverted forest.
515522
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)]
516523
pub enum TerminatorKind<'tcx> {
517524
/// Block has one successor; we continue execution there.

0 commit comments

Comments
 (0)