@@ -306,49 +306,82 @@ pub struct SimplifyLocals;
306
306
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
307
307
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
308
308
trace!("running SimplifyLocals on {:?}", source);
309
- let locals = {
309
+
310
+ // First, we're going to get a count of *actual* uses for every `Local`.
311
+ // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored.
312
+ let mut used_locals = {
310
313
let read_only_cache = read_only!(body);
311
- let mut marker = DeclMarker { locals: BitSet::new_empty (body.local_decls.len()), body } ;
314
+ let mut marker = DeclMarker::new (body) ;
312
315
marker.visit_body(&read_only_cache);
313
- // Return pointer and arguments are always live
314
- marker.locals.insert(RETURN_PLACE);
315
- for arg in body.args_iter() {
316
- marker.locals.insert(arg);
317
- }
318
316
319
- marker.locals
317
+ marker.local_counts
320
318
};
321
319
322
- let map = make_local_map(&mut body.local_decls, locals);
323
- // Update references to all vars and tmps now
324
- LocalUpdater { map, tcx }.visit_body(body);
325
- body.local_decls.shrink_to_fit();
320
+ let arg_count = body.arg_count;
321
+
322
+ // Next, we're going to remove any `Local` with zero actual uses. When we remove those
323
+ // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
324
+ // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
325
+ // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
326
+ // fixedpoint where there are no more unused locals.
327
+ loop {
328
+ let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx);
329
+ remove_statements.visit_body(body);
330
+
331
+ if !remove_statements.modified {
332
+ break;
333
+ }
334
+ }
335
+
336
+ // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
337
+ let map = make_local_map(&mut body.local_decls, used_locals, arg_count);
338
+
339
+ // Only bother running the `LocalUpdater` if we actually found locals to remove.
340
+ if map.iter().any(Option::is_none) {
341
+ // Update references to all vars and tmps now
342
+ let mut updater = LocalUpdater { map, tcx };
343
+ updater.visit_body(body);
344
+
345
+ body.local_decls.shrink_to_fit();
346
+ }
326
347
}
327
348
}
328
349
329
350
/// Construct the mapping while swapping out unused stuff out from the `vec`.
330
351
fn make_local_map<V>(
331
- vec: &mut IndexVec<Local, V>,
332
- mask: BitSet<Local>,
352
+ local_decls: &mut IndexVec<Local, V>,
353
+ used_locals: IndexVec<Local, usize>,
354
+ arg_count: usize,
333
355
) -> IndexVec<Local, Option<Local>> {
334
- let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec );
356
+ let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*local_decls );
335
357
let mut used = Local::new(0);
336
- for alive_index in mask.iter() {
358
+ for (alive_index, count) in used_locals.iter_enumerated() {
359
+ // The `RETURN_PLACE` and arguments are always live.
360
+ if alive_index.as_usize() > arg_count && *count == 0 {
361
+ continue;
362
+ }
363
+
337
364
map[alive_index] = Some(used);
338
365
if alive_index != used {
339
- vec .swap(alive_index, used);
366
+ local_decls .swap(alive_index, used);
340
367
}
341
368
used.increment_by(1);
342
369
}
343
- vec .truncate(used.index());
370
+ local_decls .truncate(used.index());
344
371
map
345
372
}
346
373
347
374
struct DeclMarker<'a, 'tcx> {
348
- pub locals: BitSet <Local>,
375
+ pub local_counts: IndexVec <Local, usize >,
349
376
pub body: &'a Body<'tcx>,
350
377
}
351
378
379
+ impl<'a, 'tcx> DeclMarker<'a, 'tcx> {
380
+ pub fn new(body: &'a Body<'tcx>) -> Self {
381
+ Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body }
382
+ }
383
+ }
384
+
352
385
impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
353
386
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
354
387
// Ignore storage markers altogether, they get removed along with their otherwise unused
@@ -368,51 +401,146 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
368
401
if location.statement_index != block.statements.len() {
369
402
let stmt = &block.statements[location.statement_index];
370
403
404
+ fn can_skip_constant(c: &ty::Const<'tcx>) -> bool {
405
+ // Keep assignments from unevaluated constants around, since the
406
+ // evaluation may report errors, even if the use of the constant
407
+ // is dead code.
408
+ !matches!(c.val, ty::ConstKind::Unevaluated(..))
409
+ }
410
+
411
+ fn can_skip_operand(o: &Operand<'_>) -> bool {
412
+ match o {
413
+ Operand::Copy(_) | Operand::Move(_) => true,
414
+ Operand::Constant(c) => can_skip_constant(c.literal),
415
+ }
416
+ }
417
+
371
418
if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind {
372
419
if !dest.is_indirect() && dest.local == *local {
373
- if let Rvalue::Use(Operand::Constant(c)) = rvalue {
374
- match c.literal.val {
375
- // Keep assignments from unevaluated constants around, since the
376
- // evaluation may report errors, even if the use of the constant
377
- // is dead code.
378
- ty::ConstKind::Unevaluated(..) => {}
379
- _ => {
380
- trace!("skipping store of const value {:?} to {:?}", c, dest);
381
- return;
382
- }
420
+ let can_skip = match rvalue {
421
+ Rvalue::Use(op) => can_skip_operand(op),
422
+ Rvalue::Discriminant(_) => true,
423
+ Rvalue::BinaryOp(_, l, r) | Rvalue::CheckedBinaryOp(_, l, r) => {
424
+ can_skip_operand(l) && can_skip_operand(r)
383
425
}
384
- } else if let Rvalue::Discriminant(d) = rvalue {
385
- trace!("skipping store of discriminant value {:?} to {:?}", d, dest);
426
+ Rvalue::Repeat(op, c) => can_skip_operand(op) && can_skip_constant(c),
427
+ Rvalue::AddressOf(_, _) => true,
428
+ Rvalue::Len(_) => true,
429
+ Rvalue::UnaryOp(_, op) => can_skip_operand(op),
430
+ Rvalue::Aggregate(_, operands) => operands.iter().all(can_skip_operand),
431
+
432
+ _ => false,
433
+ };
434
+
435
+ if can_skip {
436
+ trace!("skipping store of {:?} to {:?}", rvalue, dest);
386
437
return;
387
438
}
388
439
}
389
440
}
390
441
}
391
442
}
392
443
393
- self.locals.insert( *local) ;
444
+ self.local_counts[ *local] += 1 ;
394
445
}
395
446
}
396
447
397
- struct LocalUpdater<'tcx> {
398
- map: IndexVec<Local, Option<Local>>,
448
+ struct StatementDeclMarker<'a, 'tcx> {
449
+ used_locals: &'a mut IndexVec<Local, usize>,
450
+ statement: &'a Statement<'tcx>,
451
+ }
452
+
453
+ impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> {
454
+ pub fn new(
455
+ used_locals: &'a mut IndexVec<Local, usize>,
456
+ statement: &'a Statement<'tcx>,
457
+ ) -> Self {
458
+ Self { used_locals, statement }
459
+ }
460
+ }
461
+
462
+ impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> {
463
+ fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
464
+ // Skip the lvalue for assignments
465
+ if let StatementKind::Assign(box (p, _)) = self.statement.kind {
466
+ if p.local == *local && context.is_place_assignment() {
467
+ return;
468
+ }
469
+ }
470
+
471
+ let use_count = &mut self.used_locals[*local];
472
+ // If this is the local we're removing...
473
+ if *use_count != 0 {
474
+ *use_count -= 1;
475
+ }
476
+ }
477
+ }
478
+
479
+ struct RemoveStatements<'a, 'tcx> {
480
+ used_locals: &'a mut IndexVec<Local, usize>,
481
+ arg_count: usize,
399
482
tcx: TyCtxt<'tcx>,
483
+ modified: bool,
400
484
}
401
485
402
- impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
486
+ impl<'a, 'tcx> RemoveStatements<'a, 'tcx> {
487
+ fn new(
488
+ used_locals: &'a mut IndexVec<Local, usize>,
489
+ arg_count: usize,
490
+ tcx: TyCtxt<'tcx>,
491
+ ) -> Self {
492
+ Self { used_locals, arg_count, tcx, modified: false }
493
+ }
494
+
495
+ fn keep_local(&self, l: Local) -> bool {
496
+ trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]);
497
+ l.as_usize() <= self.arg_count || self.used_locals[l] != 0
498
+ }
499
+ }
500
+
501
+ impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
403
502
fn tcx(&self) -> TyCtxt<'tcx> {
404
503
self.tcx
405
504
}
406
505
407
506
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
408
507
// Remove unnecessary StorageLive and StorageDead annotations.
409
- data.statements.retain(|stmt| match &stmt.kind {
410
- StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
411
- StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
412
- _ => true,
508
+ let mut i = 0usize;
509
+ data.statements.retain(|stmt| {
510
+ let keep = match &stmt.kind {
511
+ StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
512
+ self.keep_local(*l)
513
+ }
514
+ StatementKind::Assign(box (place, _)) => self.keep_local(place.local),
515
+ _ => true,
516
+ };
517
+
518
+ if !keep {
519
+ trace!("removing statement {:?}", stmt);
520
+ self.modified = true;
521
+
522
+ let mut visitor = StatementDeclMarker::new(self.used_locals, stmt);
523
+ visitor.visit_statement(stmt, Location { block, statement_index: i });
524
+ }
525
+
526
+ i += 1;
527
+
528
+ keep
413
529
});
530
+
414
531
self.super_basic_block_data(block, data);
415
532
}
533
+ }
534
+
535
+ struct LocalUpdater<'tcx> {
536
+ map: IndexVec<Local, Option<Local>>,
537
+ tcx: TyCtxt<'tcx>,
538
+ }
539
+
540
+ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
541
+ fn tcx(&self) -> TyCtxt<'tcx> {
542
+ self.tcx
543
+ }
416
544
417
545
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
418
546
*l = self.map[*l].unwrap();
0 commit comments