@@ -31,10 +31,10 @@ use crate::transform::MirPass;
31
31
use rustc_index:: vec:: { Idx , IndexVec } ;
32
32
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
33
33
use rustc_middle:: mir:: * ;
34
+ use rustc_middle:: ty:: ParamEnv ;
34
35
use rustc_middle:: ty:: TyCtxt ;
35
36
use smallvec:: SmallVec ;
36
- use std:: borrow:: Cow ;
37
- use std:: convert:: TryInto ;
37
+ use std:: { borrow:: Cow , convert:: TryInto } ;
38
38
39
39
pub struct SimplifyCfg {
40
40
label : String ,
@@ -326,7 +326,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
326
326
327
327
pub fn simplify_locals < ' tcx > ( body : & mut Body < ' tcx > , tcx : TyCtxt < ' tcx > ) {
328
328
// First, we're going to get a count of *actual* uses for every `Local`.
329
- let mut used_locals = UsedLocals :: new ( body) ;
329
+ let mut used_locals = UsedLocals :: new ( body, tcx ) ;
330
330
331
331
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
332
332
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
@@ -336,7 +336,8 @@ pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
336
336
remove_unused_definitions ( & mut used_locals, body) ;
337
337
338
338
// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
339
- let map = make_local_map ( & mut body. local_decls , & used_locals) ;
339
+ let arg_count = body. arg_count . try_into ( ) . unwrap ( ) ;
340
+ let map = make_local_map ( & mut body. local_decls , & used_locals, arg_count) ;
340
341
341
342
// Only bother running the `LocalUpdater` if we actually found locals to remove.
342
343
if map. iter ( ) . any ( Option :: is_none) {
@@ -349,54 +350,61 @@ pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
349
350
}
350
351
351
352
/// Construct the mapping while swapping out unused stuff out from the `vec`.
352
- fn make_local_map < V > (
353
+ fn make_local_map < ' tcx , V > (
353
354
local_decls : & mut IndexVec < Local , V > ,
354
- used_locals : & UsedLocals ,
355
+ used_locals : & UsedLocals < ' tcx > ,
356
+ arg_count : u32 ,
355
357
) -> IndexVec < Local , Option < Local > > {
356
- let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
358
+ let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , local_decls) ;
357
359
let mut used = Local :: new ( 0 ) ;
358
360
359
361
for alive_index in local_decls. indices ( ) {
360
- // `is_used` treats the `RETURN_PLACE` and arguments as used.
361
- if !used_locals. is_used ( alive_index) {
362
- continue ;
363
- }
364
-
365
- map[ alive_index] = Some ( used) ;
366
- if alive_index != used {
367
- local_decls. swap ( alive_index, used) ;
362
+ // When creating the local map treat the `RETURN_PLACE` and arguments as used.
363
+ if alive_index. as_u32 ( ) <= arg_count || used_locals. is_used ( alive_index) {
364
+ map[ alive_index] = Some ( used) ;
365
+ if alive_index != used {
366
+ local_decls. swap ( alive_index, used) ;
367
+ }
368
+ used. increment_by ( 1 ) ;
368
369
}
369
- used. increment_by ( 1 ) ;
370
370
}
371
371
local_decls. truncate ( used. index ( ) ) ;
372
372
map
373
373
}
374
374
375
375
/// Keeps track of used & unused locals.
376
- struct UsedLocals {
376
+ struct UsedLocals < ' tcx > {
377
377
increment : bool ,
378
- arg_count : u32 ,
379
378
use_count : IndexVec < Local , u32 > ,
379
+ is_static : bool ,
380
+ local_decls : IndexVec < Local , LocalDecl < ' tcx > > ,
381
+ param_env : ParamEnv < ' tcx > ,
382
+ tcx : TyCtxt < ' tcx > ,
380
383
}
381
384
382
- impl UsedLocals {
385
+ impl UsedLocals < ' tcx > {
383
386
/// Determines which locals are used & unused in the given body.
384
- fn new ( body : & Body < ' _ > ) -> Self {
387
+ fn new ( body : & Body < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
388
+ let def_id = body. source . def_id ( ) ;
389
+ let is_static = tcx. is_static ( def_id) ;
390
+ let param_env = tcx. param_env ( def_id) ;
391
+ let local_decls = body. local_decls . clone ( ) ;
385
392
let mut this = Self {
386
393
increment : true ,
387
- arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
388
394
use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
395
+ is_static,
396
+ local_decls,
397
+ param_env,
398
+ tcx,
389
399
} ;
390
400
this. visit_body ( body) ;
391
401
this
392
402
}
393
403
394
404
/// Checks if local is used.
395
- ///
396
- /// Return place and arguments are always considered used.
397
405
fn is_used ( & self , local : Local ) -> bool {
398
406
trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
399
- local . as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
407
+ self . use_count [ local] != 0
400
408
}
401
409
402
410
/// Updates the use counts to reflect the removal of given statement.
@@ -424,7 +432,7 @@ impl UsedLocals {
424
432
}
425
433
}
426
434
427
- impl Visitor < ' _ > for UsedLocals {
435
+ impl Visitor < ' tcx > for UsedLocals < ' tcx > {
428
436
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
429
437
match statement. kind {
430
438
StatementKind :: LlvmInlineAsm ( ..)
@@ -451,7 +459,21 @@ impl Visitor<'_> for UsedLocals {
451
459
}
452
460
}
453
461
454
- fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
462
+ fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , _location : Location ) {
463
+ debug ! ( "local: {:?} is_static: {:?}, ctx: {:?}" , local, self . is_static, ctx) ;
464
+ // Do not count _0 as a used in `return;` if it is a ZST.
465
+ let return_place = * local == RETURN_PLACE
466
+ && matches ! ( ctx, PlaceContext :: NonMutatingUse ( visit:: NonMutatingUseContext :: Move ) ) ;
467
+ if !self . is_static && return_place {
468
+ let ty = self . local_decls [ * local] . ty ;
469
+ let param_env_and = self . param_env . and ( ty) ;
470
+ if let Ok ( layout) = self . tcx . layout_of ( param_env_and) {
471
+ debug ! ( "layout.is_zst: {:?}" , layout. is_zst( ) ) ;
472
+ if layout. is_zst ( ) {
473
+ return ;
474
+ }
475
+ }
476
+ }
455
477
if self . increment {
456
478
self . use_count [ * local] += 1 ;
457
479
} else {
@@ -462,7 +484,10 @@ impl Visitor<'_> for UsedLocals {
462
484
}
463
485
464
486
/// Removes unused definitions. Updates the used locals to reflect the changes made.
465
- fn remove_unused_definitions < ' a , ' tcx > ( used_locals : & ' a mut UsedLocals , body : & mut Body < ' tcx > ) {
487
+ fn remove_unused_definitions < ' a , ' tcx > (
488
+ used_locals : & ' a mut UsedLocals < ' tcx > ,
489
+ body : & mut Body < ' tcx > ,
490
+ ) {
466
491
// The use counts are updated as we remove the statements. A local might become unused
467
492
// during the retain operation, leading to a temporary inconsistency (storage statements or
468
493
// definitions referencing the local might remain). For correctness it is crucial that this
0 commit comments