@@ -367,7 +367,8 @@ impl LivenessAnalysis {
367367 }
368368
369369 /// Process a value's definition, removing it from the currently-live set.
370- fn process_def ( & mut self , val : ir:: Value ) {
370+ fn process_def ( & mut self , func : & Function , val : ir:: Value ) {
371+ debug_assert ! ( !func. dfg. value_is_alias( val) ) ;
371372 if self . currently_live . remove ( & val) {
372373 log:: trace!( "liveness: defining {val:?}, removing it from the live set" ) ;
373374 }
@@ -385,6 +386,7 @@ impl LivenessAnalysis {
385386 // Keep order deterministic since we add stack map entries in this
386387 // order.
387388 live. sort ( ) ;
389+ debug_assert ! ( live. iter( ) . all( |v| !func. dfg. value_is_alias( * v) ) ) ;
388390
389391 self . live_across_any_safepoint . extend ( live. iter ( ) . copied ( ) ) ;
390392 self . safepoints . insert ( inst, live) ;
@@ -393,6 +395,7 @@ impl LivenessAnalysis {
393395 /// Process a use of a needs-stack-map value, inserting it into the
394396 /// currently-live set.
395397 fn process_use ( & mut self , func : & Function , inst : Inst , val : Value ) {
398+ debug_assert ! ( !func. dfg. value_is_alias( val) ) ;
396399 if self . currently_live . insert ( val) {
397400 log:: trace!(
398401 "liveness: found use of {val:?}, marking it live: {inst:?}: {}" ,
@@ -423,7 +426,7 @@ impl LivenessAnalysis {
423426 while let Some ( inst) = option_inst {
424427 // Process any needs-stack-map values defined by this instruction.
425428 for val in func. dfg . inst_results ( inst) {
426- self . process_def ( * val) ;
429+ self . process_def ( func , * val) ;
427430 }
428431
429432 // If this instruction is a safepoint and we've been asked to record
@@ -447,7 +450,7 @@ impl LivenessAnalysis {
447450 // After we've processed this block's instructions, remove its
448451 // parameters from the live set. This is part of step (1).
449452 for val in func. dfg . block_params ( block) {
450- self . process_def ( * val) ;
453+ self . process_def ( func , * val) ;
451454 }
452455 }
453456
@@ -482,6 +485,11 @@ impl LivenessAnalysis {
482485 let successor_index = usize:: try_from ( successor_index) . unwrap ( ) ;
483486 self . live_outs [ block_index] . extend ( self . live_ins [ successor_index] . iter ( ) . copied ( ) ) ;
484487 }
488+ debug_assert ! (
489+ self . live_outs[ block_index]
490+ . iter( )
491+ . all( |v| !func. dfg. value_is_alias( * v) )
492+ ) ;
485493
486494 // Process the block to compute its live-in set, but do not record
487495 // safepoints yet, as we haven't yet processed loop back edges (see
@@ -491,6 +499,11 @@ impl LivenessAnalysis {
491499 // The live-in set for a block is the set of values that are still
492500 // live after the block's instructions have been processed.
493501 self . live_ins [ block_index] . extend ( self . currently_live . iter ( ) . copied ( ) ) ;
502+ debug_assert ! (
503+ self . live_ins[ block_index]
504+ . iter( )
505+ . all( |v| !func. dfg. value_is_alias( * v) )
506+ ) ;
494507
495508 // If the live-in set changed, then we need to revisit all this
496509 // block's predecessors.
@@ -552,6 +565,7 @@ impl StackSlots {
552565 }
553566
554567 fn get_or_create_stack_slot ( & mut self , func : & mut Function , val : ir:: Value ) -> ir:: StackSlot {
568+ debug_assert ! ( !func. dfg. value_is_alias( val) ) ;
555569 * self . stack_slots . entry ( val) . or_insert_with ( || {
556570 log:: trace!( "rewriting: {val:?} needs a stack slot" ) ;
557571 let ty = func. dfg . value_type ( val) ;
@@ -631,6 +645,7 @@ impl SafepointSpiller {
631645 ///
632646 /// The given cursor must point just after this value's definition.
633647 fn rewrite_def ( & mut self , pos : & mut FuncCursor < ' _ > , val : ir:: Value ) {
648+ debug_assert ! ( !pos. func. dfg. value_is_alias( val) ) ;
634649 if let Some ( slot) = self . stack_slots . get ( val) {
635650 let i = pos. ins ( ) . stack_store ( val, slot, 0 ) ;
636651 log:: trace!(
@@ -664,6 +679,8 @@ impl SafepointSpiller {
664679 . expect ( "should only call `rewrite_safepoint` on safepoint instructions" ) ;
665680
666681 for val in live {
682+ debug_assert ! ( !func. dfg. value_is_alias( * val) ) ;
683+
667684 // Get or create the stack slot for this live needs-stack-map value.
668685 let slot = self . stack_slots . get_or_create_stack_slot ( func, * val) ;
669686
@@ -692,6 +709,7 @@ impl SafepointSpiller {
692709 /// The given cursor must point just before the use of the value that we are
693710 /// replacing.
694711 fn rewrite_use ( & mut self , pos : & mut FuncCursor < ' _ > , val : & mut ir:: Value ) -> bool {
712+ debug_assert ! ( !pos. func. dfg. value_is_alias( * val) ) ;
695713 if !self . liveness . live_across_any_safepoint . contains ( * val) {
696714 return false ;
697715 }
@@ -763,6 +781,7 @@ impl SafepointSpiller {
763781 vals. extend ( pos. func . dfg . inst_values ( inst) ) ;
764782 let mut replaced_any = false ;
765783 for val in & mut vals {
784+ * val = pos. func . dfg . resolve_aliases ( * val) ;
766785 replaced_any |= self . rewrite_use ( & mut pos, val) ;
767786 }
768787 if replaced_any {
@@ -1846,13 +1865,15 @@ block0(v0: i32):
18461865 v2 -> v1
18471866 v4 -> v1
18481867 stack_store v1, ss0 ; v1 = 42
1849- v13 = stack_load.i32 ss0
1850- call fn0(v13 ), stack_map=[i32 @ ss0+0]
1868+ v17 = stack_load.i32 ss0
1869+ call fn0(v17 ), stack_map=[i32 @ ss0+0]
18511870 brif v0, block1, block2
18521871
18531872block1:
1854- call fn0(v2), stack_map=[i32 @ ss0+0] ; v2 = 42
1855- call fn0(v2) ; v2 = 42
1873+ v12 = stack_load.i32 ss0
1874+ call fn0(v12), stack_map=[i32 @ ss0+0]
1875+ v11 = stack_load.i32 ss0
1876+ call fn0(v11)
18561877 v3 = iconst.i32 36
18571878 stack_store v3, ss0 ; v3 = 36
18581879 v10 = stack_load.i32 ss0
@@ -1861,14 +1882,16 @@ block1:
18611882 jump block3(v9)
18621883
18631884block2:
1864- call fn0(v4), stack_map=[i32 @ ss0+0] ; v4 = 42
1865- call fn0(v4) ; v4 = 42
1885+ v16 = stack_load.i32 ss0
1886+ call fn0(v16), stack_map=[i32 @ ss0+0]
1887+ v15 = stack_load.i32 ss0
1888+ call fn0(v15)
18661889 v5 = iconst.i32 36
18671890 stack_store v5, ss1 ; v5 = 36
1868- v12 = stack_load.i32 ss1
1869- call fn0(v12 ), stack_map=[i32 @ ss1+0]
1870- v11 = stack_load.i32 ss1
1871- jump block3(v11 )
1891+ v14 = stack_load.i32 ss1
1892+ call fn0(v14 ), stack_map=[i32 @ ss1+0]
1893+ v13 = stack_load.i32 ss1
1894+ jump block3(v13 )
18721895
18731896block3(v6: i32):
18741897 stack_store v6, ss0
@@ -2731,38 +2754,44 @@ block3:
27312754 jump block2(v7)
27322755
27332756block4:
2734- v26 = stack_load.i32 ss1
2735- jump block5(v26 )
2757+ v32 = stack_load.i32 ss1
2758+ jump block5(v32 )
27362759
27372760block5(v20: i32):
27382761 v19 -> v20
27392762 stack_store v20, ss2
27402763 v9 = iconst.i32 0
2741- v10 = icmp.i32 eq v8, v9 ; v9 = 0
2764+ v31 = stack_load.i32 ss0
2765+ v10 = icmp eq v31, v9 ; v9 = 0
27422766 brif v10, block8(v9), block6 ; v9 = 0
27432767
27442768block6:
2745- v11 = call fn3(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2769+ v30 = stack_load.i32 ss0
2770+ v11 = call fn3(v30), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
27462771 v12 = iconst.i32 -1091584273
27472772 v13 = icmp eq v11, v12 ; v12 = -1091584273
27482773 v14 = iconst.i32 1
27492774 brif v13, block8(v14), block7 ; v14 = 1
27502775
27512776block7:
2752- v15 = call fn4(v8, v12), stack_map=[i32 @ ss0+0, i32 @ ss2+0] ; v12 = -1091584273
2777+ v29 = stack_load.i32 ss0
2778+ v15 = call fn4(v29, v12), stack_map=[i32 @ ss0+0, i32 @ ss2+0] ; v12 = -1091584273
27532779 jump block8(v15)
27542780
27552781block8(v16: i32):
27562782 trapz v16, user1
2757- call fn5(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2783+ v28 = stack_load.i32 ss0
2784+ call fn5(v28), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
27582785 v17 = call fn6(), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2759- brif v17, block5(v19), block9
2786+ v27 = stack_load.i32 ss2
2787+ brif v17, block5(v27), block9
27602788
27612789block9:
2762- v25 = stack_load.i32 ss2
2763- call fn7(v25 ), stack_map=[i32 @ ss2+0]
2790+ v26 = stack_load.i32 ss2
2791+ call fn7(v26 ), stack_map=[i32 @ ss2+0]
27642792 v23 = call fn8(), stack_map=[i32 @ ss2+0]
2765- brif v23, block10, block1(v19)
2793+ v25 = stack_load.i32 ss2
2794+ brif v23, block10, block1(v25)
27662795
27672796block10:
27682797 return
@@ -2876,4 +2905,114 @@ block10:
28762905 "try_call should have stack_map entry for v0 (spilled to ss0), got:\n {output}"
28772906 ) ;
28782907 }
2908+
2909+ #[ test]
2910+ fn rewrite_uses_of_alias_values ( ) {
2911+ let _ = env_logger:: try_init ( ) ;
2912+
2913+ let mut sig = Signature :: new ( CallConv :: SystemV ) ;
2914+ sig. params . push ( AbiParam :: new ( ir:: types:: I32 ) ) ;
2915+ let mut fn_ctx = FunctionBuilderContext :: new ( ) ;
2916+ let mut func = Function :: with_name_signature ( ir:: UserFuncName :: testcase ( "sample" ) , sig) ;
2917+ let mut builder = FunctionBuilder :: new ( & mut func, & mut fn_ctx) ;
2918+
2919+ // fn0: () -> i32 (returns a gc ref)
2920+ let name0 = builder
2921+ . func
2922+ . declare_imported_user_function ( ir:: UserExternalName {
2923+ namespace : 0 ,
2924+ index : 0 ,
2925+ } ) ;
2926+ let mut sig = Signature :: new ( CallConv :: SystemV ) ;
2927+ sig. returns . push ( AbiParam :: new ( ir:: types:: I32 ) ) ;
2928+ let signature = builder. func . import_signature ( sig) ;
2929+ let fn0 = builder. import_function ( ir:: ExtFuncData {
2930+ name : ir:: ExternalName :: user ( name0) ,
2931+ signature,
2932+ colocated : true ,
2933+ patchable : false ,
2934+ } ) ;
2935+
2936+ // fn1: (i32) -> () (consumes a gc ref)
2937+ let name1 = builder
2938+ . func
2939+ . declare_imported_user_function ( ir:: UserExternalName {
2940+ namespace : 0 ,
2941+ index : 1 ,
2942+ } ) ;
2943+ let mut sig = Signature :: new ( CallConv :: SystemV ) ;
2944+ sig. params . push ( AbiParam :: new ( ir:: types:: I32 ) ) ;
2945+ let signature = builder. func . import_signature ( sig) ;
2946+ let fn1 = builder. import_function ( ir:: ExtFuncData {
2947+ name : ir:: ExternalName :: user ( name1) ,
2948+ signature,
2949+ colocated : true ,
2950+ patchable : false ,
2951+ } ) ;
2952+
2953+ let var = builder. declare_var ( ir:: types:: I32 ) ;
2954+ builder. declare_var_needs_stack_map ( var) ;
2955+
2956+ let block0 = builder. create_block ( ) ;
2957+ let block1 = builder. create_block ( ) ;
2958+ let block2 = builder. create_block ( ) ;
2959+
2960+ builder. append_block_params_for_function_params ( block0) ;
2961+ builder. switch_to_block ( block0) ;
2962+ let v0 = builder. func . dfg . block_params ( block0) [ 0 ] ;
2963+ let inst = builder. ins ( ) . call ( fn0, & [ ] ) ;
2964+ let v1 = builder. func . dfg . first_result ( inst) ;
2965+ builder. def_var ( var, v1) ;
2966+ builder. ins ( ) . brif ( v0, block1, & [ ] , block2, & [ ] ) ;
2967+
2968+ builder. switch_to_block ( block1) ;
2969+ builder. def_var ( var, v1) ;
2970+ builder. ins ( ) . jump ( block2, & [ ] ) ;
2971+
2972+ builder. switch_to_block ( block2) ;
2973+ let v2 = builder. use_var ( var) ;
2974+ builder. ins ( ) . call ( fn1, & [ v2] ) ;
2975+ let v3 = builder. use_var ( var) ;
2976+ builder. ins ( ) . call ( fn1, & [ v3] ) ;
2977+ builder. ins ( ) . return_ ( & [ ] ) ;
2978+
2979+ builder. seal_all_blocks ( ) ;
2980+
2981+ builder. finalize ( ) ;
2982+
2983+ // The SSA construction / `Variable` infrastructure makes this value
2984+ // into an alias. But it is also an alias of a value that needs
2985+ // inclusion in stack maps, so we should reload it from the stack before
2986+ // it is passed into `fn1` in the disassembly below, rather than pass
2987+ // the original or aliased value directly.
2988+ assert ! ( func. dfg. value_is_alias( v3) ) ;
2989+ assert_eq_output ! (
2990+ func. display( ) . to_string( ) ,
2991+ r#"
2992+ function %sample(i32) system_v {
2993+ ss0 = explicit_slot 4, align = 4
2994+ sig0 = () -> i32 system_v
2995+ sig1 = (i32) system_v
2996+ fn0 = colocated u0:0 sig0
2997+ fn1 = colocated u0:1 sig1
2998+
2999+ block0(v0: i32):
3000+ v1 = call fn0()
3001+ v2 -> v1
3002+ stack_store v1, ss0
3003+ brif v0, block1, block2
3004+
3005+ block1:
3006+ jump block2
3007+
3008+ block2:
3009+ v4 = stack_load.i32 ss0
3010+ call fn1(v4), stack_map=[i32 @ ss0+0]
3011+ v3 = stack_load.i32 ss0
3012+ call fn1(v3)
3013+ return
3014+ }
3015+ "# ,
3016+ ) ;
3017+ }
28793018}
0 commit comments