Auto merge of #113124 - nbdd0121:eh_frame, r=cjgillot
Add MIR validation for unwind out from nounwind functions + fixes to make validation pass `@Nilstrieb` This is the MIR validation you asked in https://github.com/rust-lang/rust/pull/112403#discussion_r1222739722. Two passes need to be fixed to get the validation to pass: * `RemoveNoopLandingPads` currently unconditionally introduce a resume block (even there is none to begin with!), changed to not do that * Generator state transform introduces a `assert` which may unwind, and its drop elaboration also introduces many new `UnwindAction`s, so in this case run the AbortUnwindingCalls after the transformation. I believe this PR should also fix Rust-for-Linux/linux#1016, cc `@ojeda` r? `@Nilstrieb`
This commit is contained in:
commit
ff55fa3026
8 changed files with 115 additions and 15 deletions
|
@ -50,8 +50,10 @@
|
|||
//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
|
||||
//! Otherwise it drops all the values in scope at the last suspension point.
|
||||
|
||||
use crate::abort_unwinding_calls;
|
||||
use crate::deref_separator::deref_finder;
|
||||
use crate::errors;
|
||||
use crate::pass_manager as pm;
|
||||
use crate::simplify;
|
||||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
|
@ -64,6 +66,7 @@ use rustc_index::{Idx, IndexVec};
|
|||
use rustc_middle::mir::dump_mir;
|
||||
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::InstanceDef;
|
||||
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{GeneratorArgs, GenericArgsRef};
|
||||
use rustc_mir_dataflow::impls::{
|
||||
|
@ -1147,7 +1150,25 @@ fn create_generator_drop_shim<'tcx>(
|
|||
// unrelated code from the resume part of the function
|
||||
simplify::remove_dead_blocks(tcx, &mut body);
|
||||
|
||||
// Update the body's def to become the drop glue.
|
||||
// This needs to be updated before the AbortUnwindingCalls pass.
|
||||
let gen_instance = body.source.instance;
|
||||
let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None);
|
||||
let drop_instance = InstanceDef::DropGlue(drop_in_place, Some(gen_ty));
|
||||
body.source.instance = drop_instance;
|
||||
|
||||
pm::run_passes_no_validate(
|
||||
tcx,
|
||||
&mut body,
|
||||
&[&abort_unwinding_calls::AbortUnwindingCalls],
|
||||
None,
|
||||
);
|
||||
|
||||
// Temporary change MirSource to generator's instance so that dump_mir produces more sensible
|
||||
// filename.
|
||||
body.source.instance = gen_instance;
|
||||
dump_mir(tcx, false, "generator_drop", &0, &body, |_, _| Ok(()));
|
||||
body.source.instance = drop_instance;
|
||||
|
||||
body
|
||||
}
|
||||
|
@ -1317,6 +1338,8 @@ fn create_generator_resume_function<'tcx>(
|
|||
// unrelated code from the drop part of the function
|
||||
simplify::remove_dead_blocks(tcx, body);
|
||||
|
||||
pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
|
||||
|
||||
dump_mir(tcx, false, "generator_resume", &0, body, |_, _| Ok(()));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ use rustc_middle::ty::TyCtxt;
|
|||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
/// A pass that removes noop landing pads and replaces jumps to them with
|
||||
/// `None`. This is important because otherwise LLVM generates terrible
|
||||
/// code for these.
|
||||
/// `UnwindAction::Continue`. This is important because otherwise LLVM generates
|
||||
/// terrible code for these.
|
||||
pub struct RemoveNoopLandingPads;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
|
||||
|
@ -84,7 +84,17 @@ impl RemoveNoopLandingPads {
|
|||
fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
|
||||
debug!("body: {:#?}", body);
|
||||
|
||||
// make sure there's a resume block
|
||||
// Skip the pass if there are no blocks with a resume terminator.
|
||||
let has_resume = body
|
||||
.basic_blocks
|
||||
.iter_enumerated()
|
||||
.any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::Resume));
|
||||
if !has_resume {
|
||||
debug!("remove_noop_landing_pads: no resume block in MIR");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure there's a resume block without any statements
|
||||
let resume_block = {
|
||||
let mut patch = MirPatch::new(body);
|
||||
let resume_block = patch.resume_block();
|
||||
|
|
|
@ -71,8 +71,17 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
|||
// of this function. Is this intentional?
|
||||
if let Some(ty::Generator(gen_def_id, args, _)) = ty.map(Ty::kind) {
|
||||
let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
|
||||
let body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
|
||||
let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
|
||||
debug!("make_shim({:?}) = {:?}", instance, body);
|
||||
|
||||
// Run empty passes to mark phase change and perform validation.
|
||||
pm::run_passes(
|
||||
tcx,
|
||||
&mut body,
|
||||
&[],
|
||||
Some(MirPhase::Runtime(RuntimePhase::Optimized)),
|
||||
);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue