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}