3
3
/// This pass demotes 'by-value' function return types to 'by-reference` pointer types, based on
4
4
/// target specific parameters.
5
5
///
6
- /// An extra argument pointer is added to the function and this pointer is also returned. The
7
- /// return value is mem_copied to the new argument instead of being returned by value.
6
+ /// An extra argument pointer is added to the function.
7
+ /// The return value is mem_copied to the new argument instead of being returned by value.
8
8
use crate :: {
9
- AnalysisResults , BlockArgument , Context , Function , InstOp , Instruction , InstructionInserter ,
10
- IrError , Module , Pass , PassMutability , ScopedPass , Type , Value ,
9
+ AnalysisResults , BlockArgument , ConstantContent , Context , Function , InstOp , Instruction ,
10
+ InstructionInserter , IrError , Module , Pass , PassMutability , ScopedPass , Type , Value ,
11
11
} ;
12
12
13
13
pub const RET_DEMOTION_NAME : & str = "ret-demotion" ;
@@ -23,7 +23,7 @@ pub fn create_ret_demotion_pass() -> Pass {
23
23
24
24
pub fn ret_val_demotion (
25
25
context : & mut Context ,
26
- _ : & AnalysisResults ,
26
+ _analyses : & AnalysisResults ,
27
27
module : Module ,
28
28
) -> Result < bool , IrError > {
29
29
// This is a module pass because we need to update all the callers of a function if we change
@@ -39,14 +39,18 @@ pub fn ret_val_demotion(
39
39
40
40
changed = true ;
41
41
42
- // Change the function signature. It now returns a pointer.
42
+ // Change the function signature.
43
43
let ptr_ret_type = Type :: new_typed_pointer ( context, ret_type) ;
44
- function . set_return_type ( context, ptr_ret_type ) ;
44
+ let unit_ty = Type :: get_unit ( context) ;
45
45
46
46
// The storage for the return value must be determined. For entry-point functions it's a new
47
47
// local and otherwise it's an extra argument.
48
48
let entry_block = function. get_entry_block ( context) ;
49
49
let ptr_arg_val = if function. is_entry ( context) {
50
+ // Entry functions return a pointer to the original return type.
51
+ function. set_return_type ( context, ptr_ret_type) ;
52
+
53
+ // Create a local variable to hold the return value.
50
54
let ret_var = function. new_unique_local_var (
51
55
context,
52
56
"__ret_value" . to_owned ( ) ,
@@ -61,6 +65,9 @@ pub fn ret_val_demotion(
61
65
entry_block. prepend_instructions ( context, vec ! [ get_ret_var] ) ;
62
66
get_ret_var
63
67
} else {
68
+ // non-entry functions now return unit.
69
+ function. set_return_type ( context, unit_ty) ;
70
+
64
71
let ptr_arg_val = Value :: new_argument (
65
72
context,
66
73
BlockArgument {
@@ -102,10 +109,20 @@ pub fn ret_val_demotion(
102
109
. append ( context)
103
110
. store ( ptr_arg_val, ret_val)
104
111
. add_metadatum ( context, md_idx) ;
105
- ret_block
106
- . append ( context)
107
- . ret ( ptr_arg_val, ptr_ret_type)
108
- . add_metadatum ( context, md_idx) ;
112
+
113
+ if !function. is_entry ( context) {
114
+ let unit_ret = ConstantContent :: get_unit ( context) ;
115
+ ret_block
116
+ . append ( context)
117
+ . ret ( unit_ret, unit_ty)
118
+ . add_metadatum ( context, md_idx) ;
119
+ } else {
120
+ // Entry functions still return the pointer to the return value.
121
+ ret_block
122
+ . append ( context)
123
+ . ret ( ptr_arg_val, ptr_ret_type)
124
+ . add_metadatum ( context, md_idx) ;
125
+ }
109
126
}
110
127
111
128
// If the function isn't an entry point we need to update all the callers to pass the extra
@@ -180,7 +197,7 @@ fn update_callers(context: &mut Context, function: Function, ret_type: Type) {
180
197
Value :: new_instruction ( context, calling_block, InstOp :: Call ( function, new_args) ) ;
181
198
182
199
// And finally load the value from the new local var.
183
- let load_val = Value :: new_instruction ( context, calling_block, InstOp :: Load ( new_call_val ) ) ;
200
+ let load_val = Value :: new_instruction ( context, calling_block, InstOp :: Load ( get_loc_val ) ) ;
184
201
185
202
calling_block
186
203
. replace_instruction ( context, call_val, get_loc_val, false )
0 commit comments