1use crate::code_bld::CodeBuilder;
6use crate::ctx::Context;
7use crate::layout::layout_variables;
8use crate::reg_pool::HwmTempRegisterPool;
9use crate::state::GenOptions;
10use crate::top_state::TopLevelGenState;
11use crate::{
12 FunctionInData, FunctionIp, FunctionIpKind, GenFunctionInfo, MAX_REGISTER_INDEX_FOR_PARAMETERS,
13 RepresentationOfRegisters, SpilledRegisterRegion,
14};
15use source_map_cache::SourceMapWrapper;
16use source_map_node::Node;
17use std::collections::HashSet;
18use swamp_semantic::{InternalFunctionDefinitionRef, InternalMainExpression, formal_function_name};
19use swamp_vm_debug_info::FunctionDebugInfo;
20use swamp_vm_instr_build::InstructionBuilder;
21use swamp_vm_types::types::{
22 Destination, FunctionInfo, FunctionInfoKind, TypedRegister, VariableRegister, VmType,
23 VmTypeOrigin,
24};
25use swamp_vm_types::{
26 InstructionPosition, InstructionPositionOffset, InstructionRange, MemoryLocation, MemoryOffset,
27 PatchPosition,
28};
29
30impl TopLevelGenState {
31 pub fn emit_function_def(
34 &mut self,
35 internal_fn_def: &InternalFunctionDefinitionRef,
36 source_map_wrapper: &SourceMapWrapper,
37 should_ignore_host_call: bool,
38 ) {
39 assert_ne!(internal_fn_def.program_unique_id, 0);
40
41 let complete_function_name = formal_function_name(internal_fn_def);
42 let in_data = FunctionInData {
45 function_name_node: internal_fn_def.name.0.clone(),
46 kind: FunctionInfoKind::Normal(internal_fn_def.program_unique_id as usize),
47 assigned_name: complete_function_name,
48 function_variables: internal_fn_def.function_variables.clone(),
49 return_type: internal_fn_def.signature.return_type.clone(),
50 expression: internal_fn_def.body.clone(),
51 };
52
53 let attrs = &internal_fn_def.attributes;
54
55 let should_insert_halt =
56 Self::is_test_call(attrs) || (!should_ignore_host_call && Self::is_host_call(attrs));
57
58 let (start_ip, end_ip, function_info) =
59 self.emit_function_preamble(&in_data, source_map_wrapper, should_insert_halt);
60
61 let count_ip = end_ip.0 - start_ip.0;
62
63 let range = InstructionRange {
64 start: start_ip,
65 count: InstructionPositionOffset(count_ip),
66 };
67
68 self.codegen_state
69 .function_infos
70 .insert(
71 internal_fn_def.program_unique_id,
72 GenFunctionInfo {
73 ip_range: range.clone(),
74 return_type: self.codegen_state.layout_cache.layout(&in_data.return_type),
75 internal_function_definition: internal_fn_def.clone(),
76 },
77 )
78 .unwrap();
79
80 self.codegen_state.function_ips.ranges.push(FunctionIp {
81 ip_range: range.clone(),
82 kind: FunctionIpKind::Normal(internal_fn_def.program_unique_id),
83 });
84
85 self.codegen_state
86 .debug_info
87 .function_table
88 .entries
89 .push(FunctionDebugInfo {
90 start_pc: range.start.0,
91 function_id: internal_fn_def.program_unique_id,
92 });
93
94 self.codegen_state
95 .debug_info
96 .function_infos
97 .insert(internal_fn_def.program_unique_id, function_info)
98 .unwrap();
99 }
100
101 pub fn emit_main_function(
104 &mut self,
105 main: &InternalMainExpression,
106 options: &GenOptions,
107 source_map_lookup: &SourceMapWrapper,
108 ) {
109 let variable_and_frame_memory = layout_variables(
110 &mut self.codegen_state.layout_cache,
111 &main.expression.node,
112 &main.scopes,
113 &main.expression.ty,
114 );
115
116 let in_data = FunctionInData {
117 function_name_node: main.expression.node.clone(),
118 kind: FunctionInfoKind::Normal(main.program_unique_id as usize),
119 assigned_name: "main_expr".to_string(),
120 function_variables: main.scopes.clone(),
121 return_type: main.expression.ty.clone(),
122 expression: main.expression.clone(),
123 };
124
125 let (start_ip, end_ip, function_info) =
126 self.emit_function_preamble(&in_data, source_map_lookup, true);
127
128 let function_info = FunctionInfo {
129 kind: FunctionInfoKind::Normal(main.program_unique_id as usize),
130 frame_memory: variable_and_frame_memory.frame_memory,
131 return_type: variable_and_frame_memory.return_type,
132 name: "main".to_string(),
133 ip_range: InstructionRange {
134 start: start_ip,
135 count: InstructionPositionOffset(end_ip.0 - start_ip.0),
136 },
137 };
138 }
139
140 #[must_use]
141 pub const fn is_callee_save(reg_index: u8) -> bool {
142 reg_index > MAX_REGISTER_INDEX_FOR_PARAMETERS
143 }
144
145 pub fn spill_callee_save_registers(
146 code_builder: &mut CodeBuilder,
147 function_info: &FunctionInfo,
148 node: &Node,
149 ) -> Option<SpilledRegisterRegion> {
150 let mut registers_to_save: Vec<u8> = Vec::new();
152
153 for variable_register in &function_info.frame_memory.variable_registers {
154 if Self::is_callee_save(variable_register.register.index)
155 && !variable_register.register.ty.is_mutable_primitive()
156 {
157 registers_to_save.push(variable_register.register.index);
158 }
159 }
160
161 if registers_to_save.is_empty() {
162 return None;
163 }
164
165 registers_to_save.sort_unstable();
167 registers_to_save.dedup();
168
169 let count = registers_to_save.len() as u8;
170
171 let abi_parameter_frame_memory_region = code_builder.temp_frame_space_for_register(
172 count,
173 "temporary space for callee_save (and not mutable primitives)",
174 );
175
176 let start_reg = registers_to_save[0];
179
180 code_builder.builder.add_st_contiguous_regs_to_frame(
181 abi_parameter_frame_memory_region,
182 start_reg,
183 count,
184 node,
185 "prologue, spill contiguous range of callee-save registers to stack frame memory",
186 );
187
188 Some(SpilledRegisterRegion {
189 registers: RepresentationOfRegisters::Range { start_reg, count },
190 frame_memory_region: abi_parameter_frame_memory_region,
191 })
192 }
193 pub fn initialize_and_clear_variables_that_are_on_the_frame(
194 instruction_builder: &mut InstructionBuilder,
195 variable_registers: &[VariableRegister],
196 node: &Node,
197 ) {
198 for variable_reg in variable_registers {
199 if let VmTypeOrigin::Frame(frame_region) = variable_reg.register.ty.origin {
200 instruction_builder.add_lea_from_frame_region(
201 &variable_reg.register,
202 frame_region,
203 node,
204 &format!("define frame placed register {variable_reg}"),
205 );
206
207 instruction_builder.add_frame_memory_clear(
208 frame_region,
209 node,
210 &format!("clear memory for indirect variable {variable_reg}"),
211 );
212 }
213 }
214 }
215
216 pub fn function_prologue(
217 code_builder: &mut CodeBuilder,
218 function_info: &FunctionInfo,
219 node: &Node,
220 ) -> (Option<SpilledRegisterRegion>, PatchPosition) {
221 let enter_patch_position = code_builder
222 .builder
223 .add_enter_placeholder(&Node::default(), "prologue");
224
225 let maybe_spilled = Self::spill_callee_save_registers(code_builder, function_info, node);
226
227 (maybe_spilled, enter_patch_position)
231 }
232
233 fn function_epilogue(
234 instruction_builder: &mut CodeBuilder,
235 maybe_spilled_registers: Option<SpilledRegisterRegion>,
236 node: &Node,
237 comment: &str,
238 ) {
239 if let Some(spilled_register_region) = maybe_spilled_registers {
240 instruction_builder.emit_restore_region(
241 spilled_register_region,
242 &HashSet::new(),
243 node,
244 &format!("function epilogue: {comment}"),
245 );
246 }
247 }
248
249 pub fn emit_function_preamble(
250 &mut self,
251 in_data: &FunctionInData,
252 source_map_wrapper: &SourceMapWrapper,
253 is_called_by_host: bool,
254 ) -> (InstructionPosition, InstructionPosition, FunctionInfo) {
255 let start_ip = self.ip();
256
257 let frame_and_variable_info = layout_variables(
260 &mut self.codegen_state.layout_cache,
261 &in_data.function_name_node,
262 &in_data.function_variables,
263 &in_data.return_type,
264 );
265
266 let return_basic_type = self.codegen_state.layout_cache.layout(&in_data.return_type);
268
269 let mut function_info = FunctionInfo {
270 kind: in_data.kind.clone(),
271 frame_memory: frame_and_variable_info.frame_memory,
272 return_type: frame_and_variable_info.return_type,
273 name: in_data.assigned_name.clone(),
274 ip_range: InstructionRange {
275 start: start_ip,
276 count: InstructionPositionOffset(0),
277 },
278 };
279
280 let _complete_function_info = self.codegen_state.add_function(
283 function_info.clone(),
284 &in_data.function_name_node,
285 "function",
286 );
287
288 let mut instruction_builder = InstructionBuilder::new(&mut self.builder_state);
289
290 let temp_pool = HwmTempRegisterPool::new(128, 64);
291
292 let ctx = Context::new();
293
294 let mut function_code_builder = CodeBuilder::new(
295 &mut self.codegen_state,
296 &mut instruction_builder,
297 frame_and_variable_info.parameter_and_variable_offsets,
298 temp_pool,
300 frame_and_variable_info.local_frame_allocator,
301 self.code_builder_options,
302 source_map_wrapper,
303 );
304
305 let (maybe_spilled_registers, enter_patch_position) = Self::function_prologue(
306 &mut function_code_builder,
307 &function_info,
308 &in_data.function_name_node,
309 );
310 let return_register =
311 TypedRegister::new_vm_type(0, VmType::new_unknown_placement(return_basic_type));
312
313 let destination = if return_register.ty.basic_type.is_scalar() {
314 Destination::Register(return_register)
315 } else {
316 let memory_location = MemoryLocation {
317 ty: VmType::new_unknown_placement(return_register.ty().clone()),
318 base_ptr_reg: return_register,
319 offset: MemoryOffset(0),
320 };
321 if let FunctionInfoKind::Constant(found_constant) = &in_data.kind {
322 function_code_builder.emit_initialize_memory_for_any_type(
323 &memory_location,
324 &in_data.expression.node,
325 "prepare r0 memory for constant",
326 );
327 }
328
329 Destination::Memory(memory_location)
330 };
331
332 function_code_builder.emit_expression(&destination, &in_data.expression, &ctx);
333
334 function_code_builder.patch_enter(enter_patch_position);
335
336 Self::function_epilogue(
337 &mut function_code_builder,
338 maybe_spilled_registers,
339 &in_data.expression.node,
340 "epilogue",
341 );
342
343 self.finalize_function(&GenOptions {
344 is_halt_function: is_called_by_host,
345 });
346
347 let end_ip = self.ip();
348
349 function_info.ip_range.count = InstructionPositionOffset(end_ip.0 - start_ip.0);
350
351 (start_ip, end_ip, function_info)
352 }
353
354 pub fn finalize_function(&mut self, options: &GenOptions) {
355 if options.is_halt_function {
356 self.builder_state.add_hlt(&Node::default(), "");
357 } else {
358 self.builder_state.add_ret(&Node::default(), "");
359 }
360 }
361}