swamp_code_gen/
func.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use 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    /// # Panics
29    ///
30    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        //        info!(complete_function_name, "code generating function def");
40
41        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    /// # Errors
100    ///
101    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        // Collect the actual register indices that need to be saved
150        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        // Remove duplicates and sort
165        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        // Always use contiguous range approach since callee-save registers are typically >= r6
176        // and the mask approach only works for registers 0-7
177        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        // Note: Variable initialization (LEA instructions) are now generated
227        // at the point of variable definition, not in the function prologue
228
229        (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        //info!(in_data.assigned_name, "emit_function");
257
258        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        // Get the return type layout before borrowing codegen_state mutably
266        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        // debug!(name=?in_data.assigned_name, "code generating function");
286
287        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            //frame_and_variable_info.frame_registers,
304            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}