Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 74 additions & 3 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::mir::interpret::{
};
use crate::mir::visit::Visitor;
use crate::mir::*;
use crate::ty::CoroutineArgsExt;

const INDENT: &str = " ";
/// Alignment for lining up comments following MIR statements
Expand Down Expand Up @@ -185,9 +186,6 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> {
Some(promoted) => write!(w, "::{promoted:?}`")?,
}
writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;
if let Some(ref layout) = body.coroutine_layout_raw() {
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
}
writeln!(w)?;
(self.writer.extra_data)(PassWhere::BeforeCFG, w)?;
write_user_type_annotations(self.tcx(), body, w)?;
Expand Down Expand Up @@ -429,6 +427,31 @@ fn write_scope_tree(
}
}

// Coroutine debuginfo.
if let Some(layout) = body.coroutine_layout_raw() {
for (field, name) in layout.field_names.iter_enumerated() {
let source_info = layout.field_tys[field].source_info;
if let Some(name) = name
&& source_info.scope == parent
{
Comment on lines +434 to +436
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let Some(name) = name
&& let source_info = layout.field_tys[field].source_info
&& source_info.scope == parent
{
let source_info = layout.field_tys[field].source_info;
if let Some(name) = name
&& source_info.scope == parent
{

It might be clearer to separate source_info from the condition statement?

let indented_debug_info =
format!("{0:1$}coroutine debug {2} => {3:?};", INDENT, indent, name, field);

if options.include_extra_comments {
writeln!(
w,
"{0:1$} // in {2}",
indented_debug_info,
ALIGN,
comment(tcx, source_info),
)?;
} else {
writeln!(w, "{indented_debug_info}")?;
}
}
}
}

// Local variable types.
for (local, local_decl) in body.local_decls.iter_enumerated() {
if (1..body.arg_count + 1).contains(&local.index()) {
Expand Down Expand Up @@ -530,6 +553,50 @@ impl Debug for VarDebugInfo<'_> {
}
}

fn write_coroutine_layout<'tcx>(
tcx: TyCtxt<'tcx>,
layout: &CoroutineLayout<'_>,
w: &mut dyn io::Write,
options: PrettyPrintMirOptions,
) -> io::Result<()> {
let CoroutineLayout {
field_tys,
field_names: _, // Dumped in scope tree with debug info.
variant_fields,
variant_source_info,
storage_conflicts,
} = layout;

writeln!(w, "{INDENT}coroutine layout {{")?;

for (field, CoroutineSavedTy { ty, source_info, ignore_for_traits }) in
field_tys.iter_enumerated()
{
let ignore_for_traits = if *ignore_for_traits { " (ignored for traits)" } else { "" };
let indented_body = format!("{INDENT}{INDENT}field {field:?}: {ty}{ignore_for_traits};",);
if options.include_extra_comments {
writeln!(w, "{0:ALIGN$} // in {1}", indented_body, comment(tcx, *source_info))?;
} else {
writeln!(w, "{}", indented_body)?;
}
}

writeln!(w, "{INDENT}{INDENT}variant_fields = {{")?;
for (variant, fields) in variant_fields.iter_enumerated() {
let variant_name = ty::CoroutineArgs::variant_name(variant);
let header = format!("{INDENT}{INDENT}{INDENT}{variant_name:9}({variant:?}): {fields:?},");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to figure out what is the reason of having {variant_name:9}, does the variant name have at most 9 characters?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly: because impl Debug for CoroutineLayout uses that.
Variant names are Unresumed (9), Returned (8), Poisoned (8) and Suspend followed by a number.

if options.include_extra_comments {
let source_info = variant_source_info[variant];
writeln!(w, "{0:ALIGN$} // in {1}", header, comment(tcx, source_info))?;
} else {
writeln!(w, "{}", header)?;
}
}
writeln!(w, "{INDENT}{INDENT}}}")?;
writeln!(w, "{INDENT}{INDENT}storage_conflicts = {storage_conflicts:?}")?;
writeln!(w, "{INDENT}}}")
}

/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
fn write_mir_intro<'tcx>(
Expand All @@ -541,6 +608,10 @@ fn write_mir_intro<'tcx>(
write_mir_sig(tcx, body, w)?;
writeln!(w, "{{")?;

if let Some(ref layout) = body.coroutine_layout_raw() {
write_coroutine_layout(tcx, layout, w, options)?;
}

// construct a scope tree and write it out
let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
for (index, scope_data) in body.source_scopes.iter_enumerated() {
Expand Down
76 changes: 27 additions & 49 deletions compiler/rustc_mir_transform/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1077,31 +1077,42 @@ fn compute_layout<'tcx>(
/// Replaces the entry point of `body` with a block that switches on the coroutine discriminant and
/// dispatches to blocks according to `cases`.
///
/// After this function, the former entry point of the function will be bb1.
/// After this function, the former entry point of the function will be the last block.
fn insert_switch<'tcx>(
body: &mut Body<'tcx>,
cases: Vec<(usize, BasicBlock)>,
transform: &TransformVisitor<'tcx>,
default_block: BasicBlock,
) {
let (assign, discr) = transform.get_discr(body);
let switch_targets =
SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets };

let source_info = SourceInfo::outermost(body.span);
body.basic_blocks_mut().raw.insert(
0,
BasicBlockData::new_stmts(
vec![assign],
Some(Terminator { source_info, kind: switch }),
false,
),
// MIR validation ensures that no block targets `ENTRY_BLOCK`.
#[cfg(debug_assertions)]
for bb in body.basic_blocks.iter() {
for target in bb.terminator().successors() {
assert_ne!(target, START_BLOCK);
}
}

// Add the switch as entry block, and put the former entry block at the end.
let former_entry = std::mem::replace(
&mut body.basic_blocks_mut()[START_BLOCK],
BasicBlockData::new_stmts(vec![assign], None, false),
);
let former_entry = body.basic_blocks_mut().push(former_entry);

for b in body.basic_blocks_mut().iter_mut() {
b.terminator_mut().successors_mut(|target| *target += 1);
// We may point to `START_BLOCK` in our `cases`, replace it with `former_entry`.
let mut switch_targets =
SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
for bb in switch_targets.all_targets_mut() {
if *bb == START_BLOCK {
*bb = former_entry;
}
}

let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets };
body.basic_blocks_mut()[START_BLOCK].terminator =
Some(Terminator { source_info: SourceInfo::outermost(body.span), kind: switch });
}

fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
Expand Down Expand Up @@ -1172,41 +1183,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
return false;
}

// Unwinds can only start at certain terminators.
for block in body.basic_blocks.iter() {
match block.terminator().kind {
// These never unwind.
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::CoroutineDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR, can_unwind will return true if there is a TerminatorKind::FalseUnwind, but previously it will not. Will this introduce any kind of behavioural change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FalseUnwind cannot happen at this stage of compilation.


// Resume will *continue* unwinding, but if there's no other unwinding terminator it
// will never be reached.
TerminatorKind::UnwindResume => {}

TerminatorKind::Yield { .. } => {
unreachable!("`can_unwind` called before coroutine transform")
}

// These may unwind.
TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Assert { .. } => return true,

TerminatorKind::TailCall { .. } => {
unreachable!("tail calls can't be present in generators")
}
}
}

// If we didn't find an unwinding terminator, the function cannot unwind.
false
// If we don't find an unwinding terminator, the function cannot unwind.
body.basic_blocks.iter().any(|block| block.terminator().unwind().is_some())
}

// Poison the coroutine when it unwinds
Expand Down

This file was deleted.

Loading
Loading