Skip to content

Commit ae40f3f

Browse files
committed
Various hacks around storage statements
1 parent 6685e6b commit ae40f3f

3 files changed

Lines changed: 115 additions & 71 deletions

File tree

compiler/rustc_mir_build/src/builder/scope.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ impl Scope {
268268
fn needs_cleanup(&self) -> bool {
269269
self.drops.iter().any(|drop| match drop.kind {
270270
DropKind::Value | DropKind::ForLint => true,
271-
DropKind::Storage => false,
271+
DropKind::Storage => true,
272272
})
273273
}
274274

@@ -1239,7 +1239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12391239
block,
12401240
unwind_to,
12411241
dropline_to,
1242-
is_coroutine && needs_cleanup,
1242+
true && needs_cleanup,
12431243
self.arg_count,
12441244
|v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, typing_env, v),
12451245
)
@@ -1479,7 +1479,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14791479
// path, we only need to invalidate the cache for drops that happen on
14801480
// the unwind or coroutine drop paths. This means that for
14811481
// non-coroutines we don't need to invalidate caches for `DropKind::Storage`.
1482-
let invalidate_caches = needs_drop || self.coroutine.is_some();
1482+
let invalidate_caches = needs_drop || self.coroutine.is_some() || true;
14831483
for scope in self.scopes.scopes.iter_mut().rev() {
14841484
if invalidate_caches {
14851485
scope.invalidate_cache();
@@ -1630,7 +1630,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16301630
let is_coroutine = self.coroutine.is_some();
16311631
for scope in &mut self.scopes.scopes[uncached_scope..=target] {
16321632
for drop in &scope.drops {
1633-
if is_coroutine || drop.kind == DropKind::Value {
1633+
if is_coroutine || true || drop.kind == DropKind::Value {
16341634
cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop);
16351635
}
16361636
}
@@ -2005,7 +2005,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
20052005
for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated().skip(1) {
20062006
match drop_node.data.kind {
20072007
DropKind::Storage | DropKind::ForLint => {
2008-
if is_coroutine {
2008+
if true || is_coroutine {
20092009
let unwind_drop = self
20102010
.scopes
20112011
.unwind_drops

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
904904
destination,
905905
);
906906
let dest_ty = dest.ty(caller_body, tcx);
907-
let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block));
907+
let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block, unwind));
908908
caller_body[callsite.block].statements.push(Statement::new(
909909
callsite.source_info,
910910
StatementKind::Assign(Box::new((temp, dest))),
@@ -921,12 +921,19 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
921921
} else {
922922
(
923923
true,
924-
new_call_temp(caller_body, callsite, destination.ty(caller_body, tcx).ty, return_block),
924+
new_call_temp(
925+
caller_body,
926+
callsite,
927+
destination.ty(caller_body, tcx).ty,
928+
return_block,
929+
unwind,
930+
),
925931
)
926932
};
927933

928934
// Copy the arguments if needed.
929-
let args = make_call_args(inliner, args, callsite, caller_body, &callee_body, return_block);
935+
let args =
936+
make_call_args(inliner, args, callsite, caller_body, &callee_body, return_block, unwind);
930937

931938
let mut integrator = Integrator {
932939
args: &args,
@@ -983,6 +990,25 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
983990
}
984991
caller_body[block].statements.rotate_right(n);
985992
}
993+
if let UnwindAction::Cleanup(block) = unwind {
994+
// To avoid repeated O(n) insert, push any new statements to the end and rotate
995+
// the slice once.
996+
let mut n = 0;
997+
for local in callee_body.vars_and_temps_iter().rev() {
998+
// Do this for *all* locals instead of just the always-live ones.
999+
// This is necessary because StorageDead is often not preserved on
1000+
// unwind paths.
1001+
//
1002+
// FIXME: Restrict this to only locals that are storage-live at an
1003+
// UnwindAction::Continue.
1004+
let new_local = integrator.map_local(local);
1005+
caller_body[block]
1006+
.statements
1007+
.push(Statement::new(callsite.source_info, StatementKind::StorageDead(new_local)));
1008+
n += 1;
1009+
}
1010+
caller_body[block].statements.rotate_right(n);
1011+
}
9861012

9871013
// Insert all of the (mapped) parts of the callee body into the caller.
9881014
caller_body.local_decls.extend(callee_body.drain_vars_and_temps());
@@ -1045,6 +1071,7 @@ fn make_call_args<'tcx, I: Inliner<'tcx>>(
10451071
caller_body: &mut Body<'tcx>,
10461072
callee_body: &Body<'tcx>,
10471073
return_block: Option<BasicBlock>,
1074+
unwind: UnwindAction,
10481075
) -> Box<[Local]> {
10491076
let tcx = inliner.tcx();
10501077

@@ -1080,13 +1107,15 @@ fn make_call_args<'tcx, I: Inliner<'tcx>>(
10801107
callsite,
10811108
caller_body,
10821109
return_block,
1110+
unwind,
10831111
);
10841112
let tuple = create_temp_if_necessary(
10851113
inliner,
10861114
args.next().unwrap().node,
10871115
callsite,
10881116
caller_body,
10891117
return_block,
1118+
unwind,
10901119
);
10911120
assert!(args.next().is_none());
10921121

@@ -1104,13 +1133,29 @@ fn make_call_args<'tcx, I: Inliner<'tcx>>(
11041133
let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty));
11051134

11061135
// Spill to a local to make e.g., `tmp0`.
1107-
create_temp_if_necessary(inliner, tuple_field, callsite, caller_body, return_block)
1136+
create_temp_if_necessary(
1137+
inliner,
1138+
tuple_field,
1139+
callsite,
1140+
caller_body,
1141+
return_block,
1142+
unwind,
1143+
)
11081144
});
11091145

11101146
closure_ref_arg.chain(tuple_tmp_args).collect()
11111147
} else {
11121148
args.into_iter()
1113-
.map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block))
1149+
.map(|a| {
1150+
create_temp_if_necessary(
1151+
inliner,
1152+
a.node,
1153+
callsite,
1154+
caller_body,
1155+
return_block,
1156+
unwind,
1157+
)
1158+
})
11141159
.collect()
11151160
}
11161161
}
@@ -1123,6 +1168,7 @@ fn create_temp_if_necessary<'tcx, I: Inliner<'tcx>>(
11231168
callsite: &CallSite<'tcx>,
11241169
caller_body: &mut Body<'tcx>,
11251170
return_block: Option<BasicBlock>,
1171+
unwind: UnwindAction,
11261172
) -> Local {
11271173
// Reuse the operand if it is a moved temporary.
11281174
if let Operand::Move(place) = &arg
@@ -1135,7 +1181,7 @@ fn create_temp_if_necessary<'tcx, I: Inliner<'tcx>>(
11351181
// Otherwise, create a temporary for the argument.
11361182
trace!("creating temp for argument {:?}", arg);
11371183
let arg_ty = arg.ty(caller_body, inliner.tcx());
1138-
let local = new_call_temp(caller_body, callsite, arg_ty, return_block);
1184+
let local = new_call_temp(caller_body, callsite, arg_ty, return_block, unwind);
11391185
caller_body[callsite.block].statements.push(Statement::new(
11401186
callsite.source_info,
11411187
StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg, WithRetag::Yes)))),
@@ -1149,6 +1195,7 @@ fn new_call_temp<'tcx>(
11491195
callsite: &CallSite<'tcx>,
11501196
ty: Ty<'tcx>,
11511197
return_block: Option<BasicBlock>,
1198+
unwind: UnwindAction,
11521199
) -> Local {
11531200
let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span));
11541201

@@ -1161,6 +1208,11 @@ fn new_call_temp<'tcx>(
11611208
.statements
11621209
.insert(0, Statement::new(callsite.source_info, StatementKind::StorageDead(local)));
11631210
}
1211+
if let UnwindAction::Cleanup(block) = unwind {
1212+
caller_body[block]
1213+
.statements
1214+
.insert(0, Statement::new(callsite.source_info, StatementKind::StorageDead(local)));
1215+
}
11641216

11651217
local
11661218
}

compiler/rustc_mir_transform/src/move_elimination.rs

Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ impl<'tcx> crate::MirPass<'tcx> for MoveElimination {
3939
let remapped_locals =
4040
PlaceUnification::run(tcx, body, &mut liveness_matrix, &mut unprojectable_locals);
4141

42-
dump_liveness_matrix(tcx, body, "MoveElimination.post-liveness", &points, &liveness_matrix);
42+
apply_mappings(tcx, body, &remapped_locals);
4343

44-
let storage_to_rebuild = apply_mappings(tcx, body, &remapped_locals);
44+
dump_liveness_matrix(tcx, body, "MoveElimination.post-liveness", &points, &liveness_matrix);
4545

4646
if tcx.sess.emit_lifetime_markers() {
47-
reconstruct_storage(body, &points, &liveness_matrix, &storage_to_rebuild);
47+
reconstruct_storage(body, &points, &liveness_matrix);
4848
}
4949

5050
apply_alias_fixup(tcx, body);
@@ -377,26 +377,14 @@ fn apply_mappings<'tcx>(
377377
tcx: TyCtxt<'tcx>,
378378
body: &mut Body<'tcx>,
379379
remapped_locals: &IndexVec<Local, Option<Place<'tcx>>>,
380-
) -> DenseBitSet<Local> {
381-
let mut unified_locals = DenseBitSet::new_empty(body.local_decls.len());
382-
for (src, dst) in remapped_locals.iter_enumerated() {
383-
if let Some(dst_place) = dst {
384-
unified_locals.insert(src);
385-
unified_locals.insert(dst_place.local);
386-
}
387-
}
388-
389-
let storage_to_rebuild = DenseBitSet::new_empty(body.local_decls.len());
390-
let mut rewriter = PlaceUpdater { tcx, remapped_locals, unified_locals, storage_to_rebuild };
380+
) {
381+
let mut rewriter = PlaceUpdater { tcx, remapped_locals };
391382
rewriter.visit_body_preserves_cfg(body);
392-
rewriter.storage_to_rebuild
393383
}
394384

395385
struct PlaceUpdater<'a, 'tcx> {
396386
tcx: TyCtxt<'tcx>,
397387
remapped_locals: &'a IndexVec<Local, Option<Place<'tcx>>>,
398-
unified_locals: DenseBitSet<Local>,
399-
storage_to_rebuild: DenseBitSet<Local>,
400388
}
401389

402390
impl<'tcx> MutVisitor<'tcx> for PlaceUpdater<'_, 'tcx> {
@@ -425,17 +413,11 @@ impl<'tcx> MutVisitor<'tcx> for PlaceUpdater<'_, 'tcx> {
425413

426414
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
427415
match statement.kind {
428-
StatementKind::StorageDead(local) | StatementKind::StorageLive(local)
429-
if self.unified_locals.contains(local) =>
430-
{
431-
// Only rebuild storage for locals that previously had storage
432-
// statements.
433-
let mapped_local = match self.remapped_locals[local] {
434-
Some(place) => place.local,
435-
None => local,
436-
};
437-
self.storage_to_rebuild.insert(mapped_local);
438-
416+
// Remove *all* storage statements. These are rebuilt from liveness
417+
// information later. Also, since we've preserved StorageDead in
418+
// unwind paths until now, we will want to remove those since they
419+
// hurt LLVM's codegen.
420+
StatementKind::StorageDead(_) | StatementKind::StorageLive(_) => {
439421
statement.make_nop(true);
440422
return;
441423
}
@@ -476,21 +458,21 @@ fn get_or_split_edge<'tcx>(
476458
split_bb
477459
}
478460

461+
/// Don't insert storage statements in cleanup blocks and in unreachable blocks.
462+
fn should_insert_storage<'tcx>(block_data: &BasicBlockData<'tcx>) -> bool {
463+
!block_data.is_cleanup && !matches!(block_data.terminator().kind, TerminatorKind::Unreachable)
464+
}
465+
479466
/// Re-constructs storage statements for all locals.
480467
fn reconstruct_storage<'tcx>(
481468
body: &mut Body<'tcx>,
482469
points: &DenseLocationMap,
483470
liveness_matrix: &SparseIntervalMatrix<Local, SplitPointIndex>,
484-
storage_to_rebuild: &DenseBitSet<Local>,
485471
) {
486472
let mut patcher = MirPatch::new(body);
487473
let mut split_edges: FxHashMap<(BasicBlock, BasicBlock), BasicBlock> = Default::default();
488474

489475
for local in body.local_decls.indices() {
490-
if !storage_to_rebuild.contains(local) {
491-
continue;
492-
}
493-
494476
// Arguments and return values don't use storage statements.
495477
match body.local_kind(local) {
496478
LocalKind::Arg | LocalKind::ReturnPointer => continue,
@@ -543,9 +525,8 @@ fn reconstruct_storage<'tcx>(
543525
local: Local,
544526
block: BasicBlock| {
545527
for succ in body.basic_blocks[block].terminator().successors() {
546-
// Don't emit StorageDead in cleanup blocks.
547-
if body.basic_blocks[succ].is_cleanup {
548-
continue;
528+
if !should_insert_storage(&body.basic_blocks[succ]) {
529+
return;
549530
}
550531

551532
if !row.contains(SplitPointIndex::new(
@@ -576,19 +557,24 @@ fn reconstruct_storage<'tcx>(
576557
// If the live range starts at the `Early` point then it means that
577558
// the value came from a predecessor block. A write from the first
578559
// statement would happen at the `Late` point instead.
579-
if range.start
580-
== SplitPointIndex::new(points.entry_point(start_block), SplitPointEffect::Early)
581-
{
582-
// If the local is dead at the end of any predecessor block then
583-
// emit a `StorageLive` before the terminator.
584-
emit_storage_live_in_preds(body, &mut patcher, local, start_block);
585-
} else {
586-
// Otherwise just add `StorageLive` before the statement that
587-
// starts the live range.
588-
patcher.add_statement(
589-
points.to_location(range.start.point()),
590-
StatementKind::StorageLive(local),
591-
);
560+
if should_insert_storage(&body.basic_blocks[start_block]) {
561+
if range.start
562+
== SplitPointIndex::new(
563+
points.entry_point(start_block),
564+
SplitPointEffect::Early,
565+
)
566+
{
567+
// If the local is dead at the end of any predecessor block then
568+
// emit a `StorageLive` before the terminator.
569+
emit_storage_live_in_preds(body, &mut patcher, local, start_block);
570+
} else {
571+
// Otherwise just add `StorageLive` before the statement that
572+
// starts the live range.
573+
patcher.add_statement(
574+
points.to_location(range.start.point()),
575+
StatementKind::StorageLive(local),
576+
);
577+
}
592578
}
593579

594580
// The live range may span multiple blocks because
@@ -598,24 +584,30 @@ fn reconstruct_storage<'tcx>(
598584
let mut current_block = start_block;
599585
debug_assert!(start_block <= end_block);
600586
while current_block != end_block {
601-
emit_storage_dead_in_succs(body, &mut patcher, local, current_block);
587+
if should_insert_storage(&body.basic_blocks[current_block]) {
588+
emit_storage_dead_in_succs(body, &mut patcher, local, current_block);
589+
}
602590
current_block = BasicBlock::from_usize(current_block.index() + 1);
603-
emit_storage_live_in_preds(body, &mut patcher, local, current_block);
591+
if should_insert_storage(&body.basic_blocks[current_block]) {
592+
emit_storage_live_in_preds(body, &mut patcher, local, current_block);
593+
}
604594
}
605595

606596
// We need to insert `StorageDead` after the last statement that
607597
// uses a local. If this is a terminator then we need to instead
608598
// insert it at the start of every successor block where the local
609599
// is dead on entry.
610-
if range.last.point() == points.terminator(end_block) {
611-
emit_storage_dead_in_succs(body, &mut patcher, local, current_block);
612-
} else {
613-
// Don't emit StorageDead in cleanup blocks.
614-
if !body.basic_blocks[end_block].is_cleanup {
615-
patcher.add_statement(
616-
points.to_location(range.last.point()).successor_within_block(),
617-
StatementKind::StorageDead(local),
618-
);
600+
if should_insert_storage(&body.basic_blocks[end_block]) {
601+
if range.last.point() == points.terminator(end_block) {
602+
emit_storage_dead_in_succs(body, &mut patcher, local, current_block);
603+
} else {
604+
// Don't emit StorageDead in cleanup blocks.
605+
if !body.basic_blocks[end_block].is_cleanup {
606+
patcher.add_statement(
607+
points.to_location(range.last.point()).successor_within_block(),
608+
StatementKind::StorageDead(local),
609+
);
610+
}
619611
}
620612
}
621613
}

0 commit comments

Comments
 (0)