|
102 | 102 |
|
103 | 103 | use std::borrow::Cow; |
104 | 104 |
|
| 105 | +use itertools::Itertools; |
105 | 106 | use rustc_abi::FieldIdx; |
106 | 107 | use rustc_index::bit_set::DenseBitSet; |
107 | 108 | use rustc_index::{IndexSlice, IndexVec}; |
@@ -261,6 +262,28 @@ impl RelocateUpvars { |
261 | 262 | | ty::InstanceKind::FnPtrAddrShim(..) => unreachable!(), |
262 | 263 | }; |
263 | 264 |
|
| 265 | + // HACK: in case `AddRetag` is already run, we have one `Retag` at the body entrance |
| 266 | + // so we need to make sure that first `Retag` is run. |
| 267 | + let last_retag = body.basic_blocks[START_BLOCK] |
| 268 | + .statements |
| 269 | + .iter() |
| 270 | + .find_position(|stmt| !matches!(stmt.kind, StatementKind::Retag(_, _))); |
| 271 | + let retagged_start_block = if let Some((index, _)) = last_retag { |
| 272 | + let span = body.span; |
| 273 | + let bbs = body.basic_blocks_mut(); |
| 274 | + let stmts = bbs[START_BLOCK].statements.split_off(index); |
| 275 | + let terminator = bbs[START_BLOCK].terminator.take(); |
| 276 | + let split_start_block = bbs.push(BasicBlockData::new_stmts(stmts, terminator, false)); |
| 277 | + bbs[START_BLOCK].statements.shrink_to_fit(); |
| 278 | + bbs[START_BLOCK].terminator = Some(Terminator { |
| 279 | + kind: TerminatorKind::Goto { target: split_start_block }, |
| 280 | + source_info: SourceInfo::outermost(span), |
| 281 | + }); |
| 282 | + Some(split_start_block) |
| 283 | + } else { |
| 284 | + None |
| 285 | + }; |
| 286 | + |
264 | 287 | if let Some(mir_dumper) = MirDumper::new(tcx, "RelocateUpvars", body) { |
265 | 288 | mir_dumper.set_disambiguator(&"before").dump_mir(body); |
266 | 289 | } |
@@ -308,7 +331,7 @@ impl RelocateUpvars { |
308 | 331 | SubstituteUpvarVisitor { tcx, mappings: &substitution_mapping }.visit_body(body); |
309 | 332 |
|
310 | 333 | rewrite_drop_coroutine_struct(tcx, body, &substitution_mapping); |
311 | | - insert_substitution_prologue(body, &substitution_mapping); |
| 334 | + insert_substitution_prologue(body, retagged_start_block, &substitution_mapping); |
312 | 335 | patch_missing_storage_deads(tcx, body, &substitution_mapping); |
313 | 336 | hydrate_var_debug_info(body, &substitution_mapping); |
314 | 337 | if let Some(mir_dumper) = MirDumper::new(tcx, "RelocateUpvars", body) { |
@@ -445,10 +468,11 @@ fn rewrite_drop_coroutine_struct<'tcx>( |
445 | 468 |
|
446 | 469 | fn insert_substitution_prologue<'tcx>( |
447 | 470 | body: &mut Body<'tcx>, |
| 471 | + retagged_start_block: Option<BasicBlock>, |
448 | 472 | substitution_mapping: &IndexSlice<FieldIdx, UpvarSubstitution<'tcx>>, |
449 | 473 | ) { |
450 | 474 | let mut patch = MirPatch::new(body); |
451 | | - let mut stmts = Vec::with_capacity(2 * substitution_mapping.len()); |
| 475 | + let mut stmts = Vec::with_capacity(5 * substitution_mapping.len()); |
452 | 476 | for &UpvarSubstitution { local, reloc, upvar_place, span, name: _ } in substitution_mapping { |
453 | 477 | // For each upvar-local _$i |
454 | 478 | let source_info = SourceInfo::outermost(span); |
@@ -481,22 +505,30 @@ fn insert_substitution_prologue<'tcx>( |
481 | 505 | let source_info = SourceInfo::outermost(body.span); |
482 | 506 | let prologue = patch.new_block(BasicBlockData::new_stmts( |
483 | 507 | stmts, |
484 | | - Some(Terminator { source_info, kind: TerminatorKind::Goto { target: START_BLOCK } }), |
| 508 | + Some(Terminator { |
| 509 | + source_info, |
| 510 | + kind: TerminatorKind::Goto { target: retagged_start_block.unwrap_or(START_BLOCK) }, |
| 511 | + }), |
485 | 512 | false, |
486 | 513 | )); |
487 | 514 | patch.apply(body); |
488 | 515 |
|
489 | 516 | // Manually patch so that prologue is the new entry-point |
490 | | - let preds = body.basic_blocks.predecessors()[START_BLOCK].clone(); |
491 | | - let basic_blocks = body.basic_blocks.as_mut(); |
492 | | - for pred in preds { |
493 | | - basic_blocks[pred].terminator_mut().successors_mut(|target| { |
494 | | - if *target == START_BLOCK { |
495 | | - *target = prologue; |
496 | | - } |
497 | | - }); |
| 517 | + if retagged_start_block.is_some() { |
| 518 | + let basic_blocks = body.basic_blocks.as_mut(); |
| 519 | + basic_blocks[START_BLOCK].terminator_mut().successors_mut(|target| *target = prologue); |
| 520 | + } else { |
| 521 | + let preds = body.basic_blocks.predecessors()[START_BLOCK].clone(); |
| 522 | + let basic_blocks = body.basic_blocks.as_mut(); |
| 523 | + for pred in preds { |
| 524 | + basic_blocks[pred].terminator_mut().successors_mut(|target| { |
| 525 | + if *target == START_BLOCK { |
| 526 | + *target = prologue; |
| 527 | + } |
| 528 | + }); |
| 529 | + } |
| 530 | + basic_blocks.swap(START_BLOCK, prologue); |
498 | 531 | } |
499 | | - basic_blocks.swap(START_BLOCK, prologue); |
500 | 532 | } |
501 | 533 |
|
502 | 534 | /// Occasionally there are upvar locals left without `StorageDead` because |
|
0 commit comments