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