@@ -285,6 +285,12 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
285
285
UnwindAction::Unreachable | UnwindAction::Terminate(UnwindTerminateReason::Abi) => (),
286
286
}
287
287
}
288
+
289
+ fn is_critical_call_edge(&self, target: Option<BasicBlock>, unwind: UnwindAction) -> bool {
290
+ let Some(target) = target else { return false };
291
+ matches!(unwind, UnwindAction::Cleanup(_) | UnwindAction::Terminate(_))
292
+ && self.body.basic_blocks.predecessors()[target].len() > 1
293
+ }
288
294
}
289
295
290
296
impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
@@ -425,6 +431,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
425
431
}
426
432
self.check_unwind_edge(location, *unwind);
427
433
434
+ // The code generation assumes that there are no critical call edges. The assumption
435
+ // is used to simplify inserting code that should be executed along the return edge
436
+ // from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
437
+ // the code generation should be responsible for handling it.
438
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
439
+ && self.is_critical_call_edge(*target, *unwind)
440
+ {
441
+ self.fail(
442
+ location,
443
+ format!(
444
+ "encountered critical edge in `Call` terminator {:?}",
445
+ terminator.kind,
446
+ ),
447
+ );
448
+ }
449
+
428
450
// The call destination place and Operand::Move place used as an argument might be
429
451
// passed by a reference to the callee. Consequently they must be non-overlapping
430
452
// and cannot be packed. Currently this simply checks for duplicate places.
0 commit comments