swamp_code_gen/
variable.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 swamp_semantic::{Expression, ExpressionKind, VariableRef};
8use swamp_vm_isa::MemoryOffset;
9use swamp_vm_types::MemoryLocation;
10use swamp_vm_types::types::VmTypeOrigin;
11
12impl CodeBuilder<'_> {
13    pub fn initialize_variable_the_first_time(&mut self, variable: &VariableRef) {
14        let target_register = self
15            .variable_registers
16            .get(&variable.unique_id_within_function)
17            .unwrap_or_else(|| {
18                panic!(
19                    "could not find variable id {} {}",
20                    variable.unique_id_within_function, variable.assigned_name
21                )
22            })
23            .clone();
24        // For frame-placed variables, we need to emit a LEA instruction first
25        // to initialize the register to point to the frame-allocated space
26        if let VmTypeOrigin::Frame(frame_region) = target_register.ty.origin {
27            self.builder.add_lea_from_frame_region(
28                &target_register,
29                frame_region,
30                &variable.name,
31                &format!(
32                    "initialize frame-placed variable {}",
33                    variable.assigned_name
34                ),
35            );
36
37            // Clear the memory region for aggregate types
38            self.builder.add_frame_memory_clear(
39                frame_region,
40                &variable.name,
41                &format!("clear memory for variable {}", variable.assigned_name),
42            );
43
44            // For aggregate types, we also need to initialize the collection metadata (capacity, etc.)
45            if target_register.ty.basic_type.is_aggregate() {
46                // Clone the register before moving it
47                let target_reg_clone = target_register;
48                let memory_location =
49                    MemoryLocation::new_copy_over_whole_type_with_zero_offset(target_reg_clone);
50
51                // Initialize all collections in the variable, both direct and nested
52                self.emit_initialize_memory_for_any_type(
53                    &memory_location,
54                    &variable.name,
55                    &format!(
56                        "initialize collections for variable {}",
57                        variable.assigned_name
58                    ),
59                );
60            }
61        }
62    }
63
64    pub(crate) fn emit_variable_definition(
65        &mut self,
66        variable: &VariableRef,
67        expression: &Expression,
68        ctx: &Context,
69    ) {
70        self.initialize_variable_the_first_time(variable);
71
72        // Now proceed with the variable assignment, but skip initialization since we already did it
73        self.emit_variable_assignment_with_init_flag(variable, expression, ctx, true);
74    }
75
76    pub(crate) fn emit_variable_assignment(
77        &mut self,
78        variable: &VariableRef,
79        expression: &Expression,
80        ctx: &Context,
81    ) {
82        self.emit_variable_assignment_with_init_flag(variable, expression, ctx, false);
83    }
84
85    fn emit_variable_assignment_with_init_flag(
86        &mut self,
87        variable: &VariableRef,
88        expression: &Expression,
89        ctx: &Context,
90        already_initialized: bool,
91    ) {
92        let target_register = self
93            .variable_registers
94            .get(&variable.unique_id_within_function)
95            .unwrap_or_else(|| {
96                panic!(
97                    "could not find variable id {} {}",
98                    variable.unique_id_within_function, variable.assigned_name
99                )
100            })
101            .clone();
102
103        // For primitives, always pass them using direct register assignment.
104        if target_register.ty.basic_type.is_reg_copy() {
105            self.emit_expression_into_register(
106                &target_register,
107                expression,
108                "variable scalar",
109                ctx,
110            );
111        } else {
112            let memory_location = MemoryLocation {
113                base_ptr_reg: target_register.clone(),
114                offset: MemoryOffset(0),
115                ty: target_register.ty,
116            };
117
118            // Check if this is a function call that returns an aggregate
119            // If so, we can optimize by letting the function call use the variable's space directly
120            let is_function_call_returning_aggregate =
121                matches!(
122                    &expression.kind,
123                    ExpressionKind::InternalCall(_, _)
124                        | ExpressionKind::HostCall(_, _)
125                        | ExpressionKind::PostfixChain(_, _)
126                ) && !self.state.layout_cache.layout(&expression.ty).is_scalar();
127
128            // Parameters should never be initialized since they're already set up by the calling convention
129            let is_parameter = matches!(
130                variable.variable_type,
131                swamp_semantic::VariableType::Parameter
132            );
133
134            if is_function_call_returning_aggregate || is_parameter {
135                // For function calls returning aggregates or parameters, don't initialize the memory first
136                // Let the function call write directly to the variable's allocated space
137                self.emit_expression_into_target_memory(
138                    &memory_location,
139                    expression,
140                    "variable assignment (direct function call or parameter)",
141                    ctx,
142                );
143            } else {
144                // For other expressions, initialize the memory first (unless already initialized)
145                if !already_initialized {
146                    self.emit_initialize_memory_for_any_type(
147                        &memory_location,
148                        &variable.name,
149                        "initialize variable for the first time",
150                    );
151                }
152
153                self.emit_expression_into_target_memory(
154                    &memory_location,
155                    expression,
156                    "variable assignment",
157                    ctx,
158                );
159            }
160        }
161    }
162
163    pub(crate) fn emit_variable_reassignment(
164        &mut self,
165        variable: &VariableRef,
166        expression: &Expression,
167        ctx: &Context,
168    ) {
169        self.emit_variable_assignment(variable, expression, ctx);
170    }
171}