Skip to main content

luaur_code_gen/methods/
ir_call_wrapper_x_64_call.rs

1use crate::enums::category_x_64::CategoryX64;
2use crate::enums::ir_op_kind::IrOpKind;
3use crate::enums::size_x_64::SizeX64;
4use crate::functions::same_underlying_register::same_underlying_register;
5use crate::macros::codegen_assert::CODEGEN_ASSERT;
6use crate::methods::ir_call_wrapper_x_64_find_non_interfering_argument::ir_call_wrapper_x_64_find_non_interfering_argument;
7use crate::records::call_argument::CallArgument;
8use crate::records::ir_call_wrapper_x_64::IrCallWrapperX64;
9use crate::records::ir_data::k_invalid_inst_idx;
10use crate::records::ir_inst::IrInst;
11use crate::records::operand_x_64::OperandX64;
12use crate::records::register_x_64::RegisterX64;
13use crate::records::scoped_reg_x_64::ScopedRegX64;
14
15impl IrCallWrapperX64 {
16    pub fn call(&mut self, func: &OperandX64) {
17        self.func_op = *func;
18
19        // Free the result register before handling arguments so that no live value is preserved from it
20        if self.result_reg != RegisterX64::noreg {
21            unsafe {
22                (*self.regs).free_reg(self.result_reg);
23            }
24        }
25
26        self.count_register_uses();
27
28        for i in 0..(self.arg_count as usize) {
29            let arg_ptr: *mut CallArgument = &mut self.args[i];
30
31            let source_op = unsafe { (*arg_ptr).source_op };
32
33            if source_op.kind() != IrOpKind::None {
34                let inst: *mut IrInst = unsafe { (*(*self.regs).function).as_inst_op(source_op) };
35
36                if !inst.is_null() {
37                    // Source registers are recorded separately from source operands in CallArgument.
38                    // If source is the last use of IrInst, clear the register from the operand.
39                    if unsafe { (*self.regs).is_last_use_reg(&*inst, self.inst_idx) } {
40                        unsafe {
41                            (*inst).reg_x64 = RegisterX64::noreg;
42                        }
43                    } else {
44                        // If it's not the last use and register is volatile, register ownership
45                        // is taken, which also spills the operand.
46                        let reg = unsafe { (*inst).reg_x64 };
47                        if reg.size() == SizeX64::xmmword
48                            || unsafe { (*self.regs).should_free_gpr(reg) }
49                        {
50                            unsafe {
51                                (*self.regs).take_reg(reg, k_invalid_inst_idx);
52                            }
53                        }
54                    }
55                }
56            }
57
58            let source = unsafe { (*arg_ptr).source };
59            let target = unsafe { (*arg_ptr).target };
60
61            // Immediate values are stored at the end since they are not interfering and target
62            // register can still be used temporarily.
63            if source.cat == CategoryX64::imm {
64                unsafe {
65                    (*arg_ptr).candidate = false;
66                }
67            }
68            // Arguments passed through stack can be handled immediately
69            else if target.cat == CategoryX64::mem {
70                if source.cat == CategoryX64::mem {
71                    let mut tmp = ScopedRegX64 {
72                        owner: self.regs,
73                        reg: RegisterX64::noreg,
74                    };
75                    tmp.scoped_reg_x_64_ir_reg_alloc_x_64_size_x_64(
76                        unsafe { &mut *self.regs },
77                        target.memSize,
78                    );
79
80                    self.free_source_registers(unsafe { &mut *arg_ptr });
81
82                    if source.memSize == SizeX64::none {
83                        unsafe {
84                            (*self.build)
85                                .lea_operand_x_64_operand_x_64(OperandX64::reg(tmp.reg), source);
86                        }
87                    } else {
88                        unsafe {
89                            (*self.build).mov(OperandX64::reg(tmp.reg), source);
90                        }
91                    }
92
93                    unsafe {
94                        (*self.build).mov(target, OperandX64::reg(tmp.reg));
95                    }
96
97                    tmp.free();
98                } else {
99                    self.free_source_registers(unsafe { &mut *arg_ptr });
100
101                    unsafe {
102                        (*self.build).mov(target, source);
103                    }
104                }
105
106                unsafe {
107                    (*arg_ptr).candidate = false;
108                }
109            }
110            // Skip arguments that are already in their place
111            else if source.cat == CategoryX64::reg
112                && same_underlying_register(target.base, source.base)
113            {
114                self.free_source_registers(unsafe { &mut *arg_ptr });
115
116                // If target is not used as source in other arguments, prevent register allocator
117                // from giving it out.
118                if self.get_register_uses(target.base) == 0 {
119                    unsafe {
120                        (*self.regs).take_reg(target.base, k_invalid_inst_idx);
121                    }
122                } else {
123                    // Otherwise, make sure we won't free it when last source use is completed
124                    self.add_register_use(target.base);
125                }
126
127                unsafe {
128                    (*arg_ptr).candidate = false;
129                }
130            }
131        }
132
133        // Repeat until we run out of arguments to pass
134        loop {
135            // Find target argument register that is not an active source
136            let candidate = ir_call_wrapper_x_64_find_non_interfering_argument(self);
137
138            if !candidate.is_null() {
139                // This section is only for handling register targets
140                CODEGEN_ASSERT!(unsafe { (*candidate).target.cat } == CategoryX64::reg);
141
142                self.free_source_registers(unsafe { &mut *candidate });
143
144                let target_base = unsafe { (*candidate).target.base };
145                CODEGEN_ASSERT!(self.get_register_uses(target_base) == 0);
146                unsafe {
147                    (*self.regs).take_reg(target_base, k_invalid_inst_idx);
148                }
149
150                self.move_to_target(unsafe { &mut *candidate });
151
152                unsafe {
153                    (*candidate).candidate = false;
154                }
155            } else {
156                // If all registers cross-interfere (rcx <- rdx, rdx <- rcx), one has to be renamed
157                let conflict = self.find_conflicting_target();
158                if conflict != RegisterX64::noreg {
159                    self.rename_conflicting_register(conflict);
160                } else {
161                    for i in 0..(self.arg_count as usize) {
162                        CODEGEN_ASSERT!(!self.args[i].candidate);
163                    }
164                    break;
165                }
166            }
167        }
168
169        // Handle immediate arguments last
170        for i in 0..(self.arg_count as usize) {
171            let arg_ptr: *mut CallArgument = &mut self.args[i];
172
173            if unsafe { (*arg_ptr).source.cat } == CategoryX64::imm {
174                // There could be a conflict with the function source register, make this argument
175                // a candidate to find it.
176                unsafe {
177                    (*arg_ptr).candidate = true;
178                }
179
180                let conflict = self.find_conflicting_target();
181                if conflict != RegisterX64::noreg {
182                    self.rename_conflicting_register(conflict);
183                }
184
185                let target = unsafe { (*arg_ptr).target };
186                if target.cat == CategoryX64::reg {
187                    unsafe {
188                        (*self.regs).take_reg(target.base, k_invalid_inst_idx);
189                    }
190                }
191
192                self.move_to_target(unsafe { &mut *arg_ptr });
193
194                unsafe {
195                    (*arg_ptr).candidate = false;
196                }
197            }
198        }
199
200        // Free registers used in the function call
201        let func_base = self.func_op.base;
202        let func_index = self.func_op.index;
203        self.remove_register_use(func_base);
204        self.remove_register_use(func_index);
205
206        // Just before the call is made, argument registers are all marked as free in register allocator
207        for i in 0..(self.arg_count as usize) {
208            if self.args[i].target.cat == CategoryX64::reg {
209                let target_base = self.args[i].target.base;
210                unsafe {
211                    (*self.regs).free_reg(target_base);
212                }
213            }
214        }
215
216        unsafe {
217            (*self.regs).preserve_and_free_inst_values();
218            (*self.regs).assert_all_free();
219        }
220
221        unsafe {
222            (*self.build).call_operand_x_64(self.func_op);
223        }
224
225        if self.result_reg != RegisterX64::noreg {
226            // Result register was allocated before call was made, we freed it temporarily and taking it back
227            unsafe {
228                (*self.regs).take_reg(self.result_reg, self.result_inst_idx);
229            }
230
231            // Skip move to eax/rax/xmm0 result
232            if self.result_reg.index() != 0 {
233                let return_reg = RegisterX64 {
234                    bits: self.result_reg.size() as u8,
235                };
236                unsafe {
237                    (*self.build).mov(
238                        OperandX64::reg(self.result_reg),
239                        OperandX64::reg(return_reg),
240                    );
241                }
242            }
243        }
244    }
245}