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