7
7
/// return value is mem_copied to the new argument instead of being returned by value.
8
8
use crate :: {
9
9
AnalysisResults , BlockArgument , Context , Function , InstOp , Instruction , InstructionInserter ,
10
- IrError , Pass , PassMutability , ScopedPass , Type , Value ,
10
+ IrError , Module , Pass , PassMutability , ScopedPass , Type , Value ,
11
11
} ;
12
12
13
13
pub const RET_DEMOTION_NAME : & str = "ret-demotion" ;
@@ -17,91 +17,104 @@ pub fn create_ret_demotion_pass() -> Pass {
17
17
name : RET_DEMOTION_NAME ,
18
18
descr : "Demotion of by-value function return values to by-reference" ,
19
19
deps : Vec :: new ( ) ,
20
- runner : ScopedPass :: FunctionPass ( PassMutability :: Transform ( ret_val_demotion) ) ,
20
+ runner : ScopedPass :: ModulePass ( PassMutability :: Transform ( ret_val_demotion) ) ,
21
21
}
22
22
}
23
23
24
24
pub fn ret_val_demotion (
25
25
context : & mut Context ,
26
26
_: & AnalysisResults ,
27
- function : Function ,
27
+ module : Module ,
28
28
) -> Result < bool , IrError > {
29
- // Reject non-candidate.
30
- let ret_type = function. get_return_type ( context) ;
31
- if !super :: target_fuel:: is_demotable_type ( context, & ret_type) {
32
- // Return type fits in a register.
33
- return Ok ( false ) ;
34
- }
29
+ // This is a module pass because we need to update all the callers of a function if we change
30
+ // its signature.
31
+ let mut changed = false ;
32
+ for function in module. function_iter ( context) {
33
+ // Reject non-candidate.
34
+ let ret_type = function. get_return_type ( context) ;
35
+ if !super :: target_fuel:: is_demotable_type ( context, & ret_type) {
36
+ // Return type fits in a register.
37
+ continue ;
38
+ }
39
+
40
+ changed = true ;
41
+
42
+ // Change the function signature. It now returns a pointer.
43
+ let ptr_ret_type = Type :: new_typed_pointer ( context, ret_type) ;
44
+ function. set_return_type ( context, ptr_ret_type) ;
45
+
46
+ // The storage for the return value must be determined. For entry-point functions it's a new
47
+ // local and otherwise it's an extra argument.
48
+ let entry_block = function. get_entry_block ( context) ;
49
+ let ptr_arg_val = if function. is_entry ( context) {
50
+ let ret_var = function. new_unique_local_var (
51
+ context,
52
+ "__ret_value" . to_owned ( ) ,
53
+ ret_type,
54
+ None ,
55
+ false ,
56
+ ) ;
57
+
58
+ // Insert the return value pointer at the start of the entry block.
59
+ let get_ret_var =
60
+ Value :: new_instruction ( context, entry_block, InstOp :: GetLocal ( ret_var) ) ;
61
+ entry_block. prepend_instructions ( context, vec ! [ get_ret_var] ) ;
62
+ get_ret_var
63
+ } else {
64
+ let ptr_arg_val = Value :: new_argument (
65
+ context,
66
+ BlockArgument {
67
+ block : entry_block,
68
+ idx : function. num_args ( context) ,
69
+ ty : ptr_ret_type,
70
+ } ,
71
+ ) ;
72
+ function. add_arg ( context, "__ret_value" , ptr_arg_val) ;
73
+ entry_block. add_arg ( context, ptr_arg_val) ;
74
+ ptr_arg_val
75
+ } ;
35
76
36
- // Change the function signature. It now returns a pointer.
37
- let ptr_ret_type = Type :: new_typed_pointer ( context, ret_type) ;
38
- function. set_return_type ( context, ptr_ret_type) ;
39
-
40
- // The storage for the return value must be determined. For entry-point functions it's a new
41
- // local and otherwise it's an extra argument.
42
- let entry_block = function. get_entry_block ( context) ;
43
- let ptr_arg_val = if function. is_entry ( context) {
44
- let ret_var =
45
- function. new_unique_local_var ( context, "__ret_value" . to_owned ( ) , ret_type, None , false ) ;
46
-
47
- // Insert the return value pointer at the start of the entry block.
48
- let get_ret_var = Value :: new_instruction ( context, entry_block, InstOp :: GetLocal ( ret_var) ) ;
49
- entry_block. prepend_instructions ( context, vec ! [ get_ret_var] ) ;
50
- get_ret_var
51
- } else {
52
- let ptr_arg_val = Value :: new_argument (
53
- context,
54
- BlockArgument {
55
- block : entry_block,
56
- idx : function. num_args ( context) ,
57
- ty : ptr_ret_type,
58
- } ,
59
- ) ;
60
- function. add_arg ( context, "__ret_value" , ptr_arg_val) ;
61
- entry_block. add_arg ( context, ptr_arg_val) ;
62
- ptr_arg_val
63
- } ;
64
-
65
- // Gather the blocks which are returning.
66
- let ret_blocks = function
67
- . block_iter ( context)
68
- . filter_map ( |block| {
69
- block. get_terminator ( context) . and_then ( |term| {
70
- if let InstOp :: Ret ( ret_val, _ty) = term. op {
71
- Some ( ( block, ret_val) )
72
- } else {
73
- None
74
- }
77
+ // Gather the blocks which are returning.
78
+ let ret_blocks = function
79
+ . block_iter ( context)
80
+ . filter_map ( |block| {
81
+ block. get_terminator ( context) . and_then ( |term| {
82
+ if let InstOp :: Ret ( ret_val, _ty) = term. op {
83
+ Some ( ( block, ret_val) )
84
+ } else {
85
+ None
86
+ }
87
+ } )
75
88
} )
76
- } )
77
- . collect :: < Vec < _ > > ( ) ;
78
-
79
- // Update each `ret` to store the return value to the 'out' arg and then return the pointer.
80
- for ( ret_block , ret_val ) in ret_blocks {
81
- // This is a special case where we're replacing the terminator. We can just pop it off the
82
- // end of the block and add new instructions.
83
- let last_instr_pos = ret_block. num_instructions ( context) - 1 ;
84
- let orig_ret_val = ret_block. get_instruction_at ( context, last_instr_pos) ;
85
- ret_block . remove_instruction_at ( context , last_instr_pos ) ;
86
- let md_idx = orig_ret_val . and_then ( |val| val . get_metadata ( context ) ) ;
87
-
88
- ret_block
89
- . append ( context )
90
- . store ( ptr_arg_val , ret_val )
91
- . add_metadatum ( context , md_idx ) ;
92
- ret_block
93
- . append ( context )
94
- . ret ( ptr_arg_val , ptr_ret_type )
95
- . add_metadatum ( context , md_idx ) ;
96
- }
97
-
98
- // If the function isn't an entry point we need to update all the callers to pass the extra
99
- // argument.
100
- if !function . is_entry ( context) {
101
- update_callers ( context , function , ret_type ) ;
89
+ . collect :: < Vec < _ > > ( ) ;
90
+
91
+ // Update each `ret` to store the return value to the 'out' arg and then return the pointer.
92
+ for ( ret_block , ret_val ) in ret_blocks {
93
+ // This is a special case where we're replacing the terminator. We can just pop it off the
94
+ // end of the block and add new instructions.
95
+ let last_instr_pos = ret_block . num_instructions ( context ) - 1 ;
96
+ let orig_ret_val = ret_block. get_instruction_at ( context, last_instr_pos ) ;
97
+ ret_block. remove_instruction_at ( context, last_instr_pos) ;
98
+ let md_idx = orig_ret_val . and_then ( |val| val . get_metadata ( context ) ) ;
99
+
100
+ ret_block
101
+ . append ( context )
102
+ . store ( ptr_arg_val , ret_val )
103
+ . add_metadatum ( context , md_idx ) ;
104
+ ret_block
105
+ . append ( context )
106
+ . ret ( ptr_arg_val , ptr_ret_type )
107
+ . add_metadatum ( context , md_idx ) ;
108
+ }
109
+
110
+ // If the function isn't an entry point we need to update all the callers to pass the extra
111
+ // argument.
112
+ if !function . is_entry ( context ) {
113
+ update_callers ( context, function , ret_type ) ;
114
+ }
102
115
}
103
116
104
- Ok ( true )
117
+ Ok ( changed )
105
118
}
106
119
107
120
fn update_callers ( context : & mut Context , function : Function , ret_type : Type ) {
0 commit comments