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