@@ -37,6 +37,40 @@ pub struct CfgSimplifyStats {
3737 /// Empty blocks whose predecessors now branch straight to their
3838 /// target.
3939 pub threaded : usize ,
40+ /// Blocks with no path from the function entry.
41+ pub unreachable_removed : usize ,
42+ }
43+
44+ /// Drop blocks left disconnected by CFG rewrites, and their stale phi edges.
45+ /// Reachability follows terminators, which are the edges codegen executes.
46+ pub fn prune_unreachable ( func : & mut HirFunction ) -> usize {
47+ let mut reachable = HashSet :: new ( ) ;
48+ let mut pending = vec ! [ func. entry_block] ;
49+ while let Some ( id) = pending. pop ( ) {
50+ if !reachable. insert ( id) {
51+ continue ;
52+ }
53+ if let Some ( block) = func. blocks . get ( & id) {
54+ pending. extend ( block. terminator . targets ( ) ) ;
55+ }
56+ }
57+ let before = func. blocks . len ( ) ;
58+ if before == reachable. len ( ) {
59+ return 0 ;
60+ }
61+ func. blocks . retain ( |id, _| reachable. contains ( id) ) ;
62+ for block in func. blocks . values_mut ( ) {
63+ block. predecessors . retain ( |id| reachable. contains ( id) ) ;
64+ block. successors . retain ( |id| reachable. contains ( id) ) ;
65+ for phi in & mut block. phis {
66+ phi. incoming . retain ( |( _, pred) | reachable. contains ( pred) ) ;
67+ }
68+ }
69+ before - func. blocks . len ( )
70+ }
71+
72+ pub fn prune_unreachable_module ( module : & mut HirModule ) -> usize {
73+ module. functions_to_optimize ( ) . map ( prune_unreachable) . sum ( )
4074}
4175
4276/// Run on one function. Iterates until no merge fires (covers chains
@@ -410,4 +444,37 @@ mod tests {
410444 let stats = run ( & mut f) ;
411445 assert_eq ! ( stats. merged, 0 ) ;
412446 }
447+
448+ #[ test]
449+ fn prune_unreachable_removes_orphan_phi_edges ( ) {
450+ let mut f = mk_func ( ) ;
451+ let entry = f. entry_block ;
452+ let join = HirId :: new ( ) ;
453+ let orphan = HirId :: new ( ) ;
454+ let live_value = add_const ( & mut f, HirType :: I64 , HirConstant :: I64 ( 7 ) ) ;
455+ let dead_value = add_const ( & mut f, HirType :: I64 , HirConstant :: I64 ( 8 ) ) ;
456+ let result = HirId :: new ( ) ;
457+ f. blocks . insert ( entry, HirBlock :: new ( entry) ) ;
458+ f. blocks . insert ( join, HirBlock :: new ( join) ) ;
459+ f. blocks . insert ( orphan, HirBlock :: new ( orphan) ) ;
460+ f. blocks . get_mut ( & entry) . unwrap ( ) . terminator = HirTerminator :: Branch { target : join } ;
461+ f. blocks . get_mut ( & entry) . unwrap ( ) . successors = vec ! [ join] ;
462+ f. blocks . get_mut ( & orphan) . unwrap ( ) . terminator = HirTerminator :: Branch { target : join } ;
463+ f. blocks . get_mut ( & orphan) . unwrap ( ) . successors = vec ! [ join] ;
464+ let join_block = f. blocks . get_mut ( & join) . unwrap ( ) ;
465+ join_block. predecessors = vec ! [ entry, orphan] ;
466+ join_block. phis . push ( crate :: hir:: HirPhi {
467+ result,
468+ ty : HirType :: I64 ,
469+ incoming : vec ! [ ( live_value, entry) , ( dead_value, orphan) ] ,
470+ } ) ;
471+ join_block. terminator = HirTerminator :: Return {
472+ values : vec ! [ result] ,
473+ } ;
474+
475+ assert_eq ! ( prune_unreachable( & mut f) , 1 ) ;
476+ assert ! ( !f. blocks. contains_key( & orphan) ) ;
477+ assert_eq ! ( f. blocks[ & join] . predecessors, vec![ entry] ) ;
478+ assert_eq ! ( f. blocks[ & join] . phis[ 0 ] . incoming, vec![ ( live_value, entry) ] ) ;
479+ }
413480}
0 commit comments