@@ -505,7 +505,6 @@ impl RequiredPhiValues {
505505 graph : & SsaValueGraph ,
506506 root : & SemanticNode ,
507507 constants : & BTreeMap < SsaVar , InsnArg > ,
508- recovered : & BTreeSet < SsaVar > ,
509508 contractions : & ControlContractions ,
510509 ) -> Result < BTreeSet < SsaVar > , SourceVariableError > {
511510 let mut collector = Self {
@@ -520,7 +519,13 @@ impl RequiredPhiValues {
520519 let edge_arguments = ContractedEdgeArguments :: new ( cfg, graph, constants, contractions) ;
521520 while let Some ( value) = pending. pop ( ) {
522521 if !visited. insert ( value)
523- || ( recovered. contains ( & value) && materialized. contains ( & value) )
522+ // A semantic statement owns its rewritten dependencies. In
523+ // particular, gated value recovery can replace a CFG move
524+ // from a Phi with a Select expression while retaining the
525+ // move's result identity. Following the stale CFG operand
526+ // would lower that already-recovered Phi a second time and
527+ // place copies before the Select's branch definitions.
528+ || materialized. contains ( & value)
524529 || PhiCopies :: canonical_constant ( constants, value) . is_some ( )
525530 {
526531 continue ;
@@ -2837,6 +2842,67 @@ mod tests {
28372842 assert_eq ! ( resolver. physical_type( moved_value) , Some ( ArgType :: BOOLEAN ) ) ;
28382843 }
28392844
2845+ #[ test]
2846+ fn semantic_select_definition_does_not_rematerialize_its_cfg_phi ( ) {
2847+ let entry = BlockId :: new ( 0 ) ;
2848+ let left_block = BlockId :: new ( 1 ) ;
2849+ let right_block = BlockId :: new ( 2 ) ;
2850+ let join = BlockId :: new ( 3 ) ;
2851+ let left = RegisterArg :: new_ssa ( 1 , 0 , ArgType :: INT ) ;
2852+ let right = RegisterArg :: new_ssa ( 1 , 1 , ArgType :: INT ) ;
2853+ let phi_result = RegisterArg :: new_ssa ( 1 , 2 , ArgType :: INT ) ;
2854+ let moved = RegisterArg :: new_ssa ( 3 , 0 , ArgType :: INT ) ;
2855+ let phi_value = SsaVar :: from_reg ( & phi_result) . expect ( "phi SSA value" ) ;
2856+
2857+ let mut cfg = CFG :: new ( "recovered_select_move" ) ;
2858+ cfg. entry = entry;
2859+ cfg. add_block ( Block :: new ( entry) ) ;
2860+ let mut left_body = Block :: new ( left_block) ;
2861+ left_body. push ( InsnNode :: const_value ( left. clone ( ) , 1 ) ) ;
2862+ cfg. add_block ( left_body) ;
2863+ let mut right_body = Block :: new ( right_block) ;
2864+ right_body. push ( InsnNode :: const_value ( right. clone ( ) , 2 ) ) ;
2865+ cfg. add_block ( right_body) ;
2866+ let mut join_body = Block :: new ( join) ;
2867+ join_body. push ( InsnNode :: phi (
2868+ phi_result. clone ( ) ,
2869+ vec ! [
2870+ ( left_block. raw( ) , InsnArg :: Reg ( left. clone( ) ) ) ,
2871+ ( right_block. raw( ) , InsnArg :: Reg ( right. clone( ) ) ) ,
2872+ ] ,
2873+ ) ) ;
2874+ join_body. push ( InsnNode :: move_insn ( moved. clone ( ) , InsnArg :: Reg ( phi_result) ) ) ;
2875+ cfg. add_block ( join_body) ;
2876+ cfg. add_edge ( entry, left_block, EdgeKind :: True ) ;
2877+ cfg. add_edge ( entry, right_block, EdgeKind :: False ) ;
2878+ cfg. add_edge ( left_block, join, EdgeKind :: Normal ) ;
2879+ cfg. add_edge ( right_block, join, EdgeKind :: Normal ) ;
2880+
2881+ let values = SsaValueGraph :: build ( & cfg) . expect ( "SSA graph" ) ;
2882+ let root = SemanticNode :: BasicBlock ( SemanticBlock {
2883+ id : join,
2884+ statements : vec ! [ SemanticStatement :: definition(
2885+ InstructionId :: new( 7 ) ,
2886+ moved,
2887+ SemanticExpression :: select(
2888+ crate :: ir:: SemanticPredicate :: True ,
2889+ SemanticExpression :: Register ( left) ,
2890+ SemanticExpression :: Register ( right) ,
2891+ ) ,
2892+ ) ] ,
2893+ } ) ;
2894+ let required = RequiredPhiValues :: collect (
2895+ & cfg,
2896+ & values,
2897+ & root,
2898+ & BTreeMap :: new ( ) ,
2899+ & ControlContractions :: identity ( ) ,
2900+ )
2901+ . expect ( "required Phi analysis" ) ;
2902+
2903+ assert ! ( !required. contains( & phi_value) ) ;
2904+ }
2905+
28402906 #[ test]
28412907 fn copy_resolver_selects_nested_phi_value_for_exception_edge ( ) {
28422908 let entry = BlockId :: new ( 3 ) ;
0 commit comments