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