swamp_code_gen/rvalue.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};
8use swamp_vm_types::types::{Place, TypedRegister, VmType};
9
10impl CodeBuilder<'_> {
11 /// Make sure we have a pointer to something, no matter if it is a scalar or aggregate
12 ///
13 /// Mostly (only?) used for making sure we have a key value memory region for
14 /// `Map` to calculate a hash for (and copy this memory for inserting)
15 pub fn emit_aggregate_pointer_or_pointer_to_scalar_memory(
16 &mut self,
17 aggregate_or_scalar_expr: &Expression,
18 ctx: &Context,
19 ) -> TypedRegister {
20 let gen_key_type = self.state.layout_cache.layout(&aggregate_or_scalar_expr.ty);
21 if gen_key_type.is_aggregate() {
22 self.emit_scalar_rvalue(aggregate_or_scalar_expr, ctx)
23 } else {
24 // for scalar values, we need to materialize in a temp memory
25 let memory_location = self.allocate_frame_space_and_return_memory_location(
26 &gen_key_type,
27 false, // do not need to clear, since scalar doesn't have padding
28 &aggregate_or_scalar_expr.node,
29 "temp space for scalar key",
30 );
31 self.emit_expression_into_target_memory(
32 &memory_location,
33 aggregate_or_scalar_expr,
34 "temp space for scalar",
35 ctx,
36 );
37 memory_location.pointer_location().unwrap().ptr_reg
38 }
39 }
40
41 /// Emits code to evaluate an expression and return a pointer register.
42 ///
43 /// This function handles both regular expressions that can be materialized as scalar rvalues
44 /// and expressions that need temporary memory storage (like initializer lists).
45 ///
46 /// For expressions that need memory materialization:
47 /// - If `allow_temporary` is true: Allocates temporary frame space, initializes it, and returns pointer
48 /// - If `allow_temporary` is false: Falls back to `emit_scalar_rvalue` (may fail for some expressions)
49 ///
50 /// For regular expressions:
51 /// - Uses `emit_scalar_rvalue` to get the pointer directly
52 ///
53 /// # Parameters
54 /// - `expr`: The expression to evaluate
55 /// - `ctx`: Code generation context
56 /// - `allow_temporary`: Whether temporary storage is acceptable (false for mutable iteration)
57 pub fn emit_scalar_rvalue_or_pointer_to_temporary(
58 &mut self,
59 expr: &Expression,
60 ctx: &Context,
61 allow_temporary: bool,
62 ) -> TypedRegister {
63 if allow_temporary
64 && Self::rvalue_needs_memory_location_to_materialize_in(
65 &mut self.state.layout_cache,
66 expr,
67 )
68 {
69 // Expression needs temporary storage (like initializer lists)
70 let expr_basic_type = self.state.layout_cache.layout(&expr.ty);
71 let temp_memory = self.allocate_frame_space_and_return_destination_to_it(
72 &expr_basic_type,
73 false, // do not need to clear, since emit_initialize_memory happens below
74 &expr.node,
75 "temporary storage for expression that needs memory materialization",
76 );
77
78 // Initialize the temporary memory for collections (vectors, etc.)
79 if let Place::Memory(ref memory_location) = temp_memory {
80 self.emit_initialize_memory_for_any_type(
81 memory_location,
82 &expr.node,
83 "initialize temporary storage for expression",
84 );
85 }
86
87 // Materialize the expression into the temporary memory
88 self.emit_expression(&temp_memory, expr, ctx);
89
90 // Return the pointer to the temporary memory
91 self.emit_compute_effective_address_to_register(
92 &temp_memory,
93 &expr.node,
94 "get pointer to temporary storage",
95 )
96 } else {
97 // Regular case: expression can be materialized as scalar rvalue
98 // Note: This may fail for expressions that need memory materialization
99 // when allow_temporary is false, but that's intentional behavior
100 self.emit_scalar_rvalue(expr, ctx)
101 }
102 }
103
104 /// Emits code to evaluate an expression into a scalar rvalue.
105 ///
106 /// In compiler terminology:
107 /// - "emit" means to generate the machine code
108 /// - "scalar" refers to single-value types (numbers, booleans, pointers) as opposed to aggregates
109 /// - "rvalue" is a value-producing expression that can appear on the right side of an assignment
110 ///
111 /// This method provides an optimized path for scalar values, avoiding the complexity
112 /// of aggregate type handling in the general `emit_expression`. It's particularly
113 /// important for efficient code generation of expressions that must produce values
114 /// (rvalues) rather than storage locations (lvalues).
115 ///
116 /// Basically, for aggregate types, it shouldn't to materialize (copy) the aggregate value
117 /// to a destination, but instead just provide a register pointing to the source location.
118 ///
119 /// # Direct Register Access
120 ///
121 /// The following cases can return a register without materialization:
122 ///
123 /// - Variable access (returns the existing register)
124 /// - Constants (the memory location can be materialized into a register)
125 /// - Some scalar literals (can be materialized directly into a register)
126 ///
127 /// # Register Allocation
128 ///
129 /// For other expressions that can't provide a direct register,
130 /// a temporary register is allocated and the expression is evaluated into it.
131 ///
132 /// # Examples in Compiler Terms
133 ///
134 /// ```ignore
135 /// // Binary operations need rvalues for both operands
136 /// let left_reg = emit_scalar_rvalue(&binary_op.left); // rvalue needed
137 /// let right_reg = emit_scalar_rvalue(&binary_op.right); // rvalue needed
138 ///
139 /// // Assignment needs an lvalue on left, rvalue on right
140 /// let target = emit_lvalue(&assign.left); // lvalue needed
141 /// let value = emit_scalar_rvalue(&assign.right); // rvalue needed
142 /// ```
143 pub fn emit_scalar_rvalue(&mut self, expr: &Expression, ctx: &Context) -> TypedRegister {
144 match &expr.kind {
145 ExpressionKind::VariableAccess(variable_ref) => {
146 return self.get_variable_register(variable_ref).clone();
147 }
148 ExpressionKind::ConstantAccess(constant_ref) => {
149 let constant_type = self.state.layout_cache.layout(&constant_ref.resolved_type);
150 if constant_type.is_aggregate() {
151 let temp_target_reg = self.temp_registers.allocate(
152 VmType::new_unknown_placement(constant_type),
153 "temporary for constant access",
154 );
155 let constant_gen = self.state.constant_offsets.get(&constant_ref.id).unwrap();
156 self.builder.add_mov_32_immediate_value(
157 temp_target_reg.register(),
158 constant_gen.addr().0,
159 &expr.node,
160 "load in the constant pointer",
161 );
162 return temp_target_reg.register;
163 }
164 }
165 _ => {}
166 }
167 {
168 let ty = self.state.layout_cache.layout(&expr.ty);
169 let temp_target_reg = self.temp_registers.allocate(
170 VmType::new_unknown_placement(ty),
171 "to produce a rvalue, we have to allocate a temporary variable",
172 );
173
174 self.emit_expression_into_register(
175 temp_target_reg.register(),
176 expr,
177 "emit_rvalue",
178 ctx,
179 );
180
181 temp_target_reg.register
182 }
183 }
184}